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.cyclone;
030    
031    import java.io.File;
032    import java.sql.Connection;
033    import java.sql.DriverManager;
034    import java.sql.ResultSet;
035    import java.sql.SQLException;
036    import java.sql.Statement;
037    import java.util.ArrayList;
038    import java.util.Calendar;
039    import java.util.GregorianCalendar;
040    import java.util.HashMap;
041    import java.util.Hashtable;
042    import java.util.List;
043    import java.util.Map;
044    
045    import ucar.unidata.data.BadDataException;
046    import ucar.unidata.data.DataSourceDescriptor;
047    import ucar.unidata.data.DataUtil;
048    import ucar.unidata.sql.SqlUtil;
049    import ucar.unidata.util.DateUtil;
050    import ucar.unidata.util.IOUtil;
051    import ucar.unidata.util.Misc;
052    import ucar.visad.Util;
053    import visad.CommonUnit;
054    import visad.DateTime;
055    import visad.Real;
056    import visad.RealType;
057    import visad.VisADException;
058    import visad.georef.EarthLocation;
059    import visad.georef.EarthLocationLite;
060    
061    /**
062     * Created by IntelliJ IDEA. User: yuanho Date: Apr 9, 2008 Time: 4:58:27 PM To
063     * change this template use File | Settings | File Templates.
064     */
065    public class STIStormDataSource extends StormDataSource {
066    
067            /** _more_ */
068            private static final Way DEFAULT_OBSERVATION_WAY = new Way("babj");
069    
070            /* Use this for mysql: */
071    
072            /** _more_ */
073            private static final String DEFAULT_URL = "jdbc:mysql://localhost:3306/typhoon?zeroDateTimeBehavior=convertToNull&user=yuanho&password=password";
074    
075            // private static final String DEFAULT_URL =
076            // "jdbc:mysql://localhost:3306/typhoon?zeroDateTimeBehavior=convertToNull&user=yuanho&password=password";
077    
078            /** _more_ */
079            private static final String DEFAULT_DERBY_URL = "jdbc:derby:test;create=true";
080    
081            /** _more_ */
082            private static final String COL_DERBY_HOUR = "hh";
083    
084            /** _more_ */
085            private static final String COL_DERBY_YEAR = "yyyy";
086    
087            /**
088             * _more_
089             * 
090             * @return _more_
091             */
092            private boolean useDerby() {
093                    if ((dbUrl != null) && (dbUrl.indexOf("derby") >= 0)) {
094                            return true;
095                    }
096                    return false;
097            }
098    
099            /**
100             * _more_
101             * 
102             * @return _more_
103             */
104            public String getId() {
105                    return "sti";
106            }
107    
108            /**
109             * _more_
110             * 
111             * @return _more_
112             */
113            private String getColHour() {
114                    if (useDerby()) {
115                            return COL_DERBY_HOUR;
116                    }
117                    return COL_TYPHOON_HOUR;
118            }
119    
120            /**
121             * _more_
122             * 
123             * @return _more_
124             */
125            private String getColYear() {
126                    if (useDerby()) {
127                            return COL_DERBY_YEAR;
128                    }
129                    return COL_TYPHOON_YEAR;
130            }
131    
132            /** _more_ */
133            public static StormParam PARAM_MAXWINDSPEED;
134    
135            /** _more_ */
136            public static StormParam PARAM_RADIUSMODERATEGALE;
137    
138            /** _more_ */
139            public static StormParam PARAM_RADIUSWHOLEGALE;
140    
141            /** _more_ */
142            public static StormParam PARAM_PROBABILITY10RADIUS;
143    
144            /** _more_ */
145            public static StormParam PARAM_PROBABILITY20RADIUS;
146    
147            /** _more_ */
148            public static StormParam PARAM_PROBABILITY30RADIUS;
149    
150            /** _more_ */
151            public static StormParam PARAM_PROBABILITY40RADIUS;
152    
153            /** _more_ */
154            public static StormParam PARAM_PROBABILITY50RADIUS;
155    
156            /** _more_ */
157            public static StormParam PARAM_PROBABILITY60RADIUS;
158    
159            /** _more_ */
160            public static StormParam PARAM_PROBABILITY70RADIUS;
161    
162            /** _more_ */
163            public static StormParam PARAM_PROBABILITY80RADIUS;
164    
165            /** _more_ */
166            public static StormParam PARAM_PROBABILITY90RADIUS;
167    
168            /** _more_ */
169            public static StormParam PARAM_DISTANCE_ERROR;
170    
171            /** _more_ */
172            public static StormParam PARAM_PROBABILITY100RADIUS;
173    
174            /** _more_ */
175            public static StormParam PARAM_PROBABILITYRADIUS;
176    
177            /** _more_ */
178            public static StormParam PARAM_MOVEDIRECTION;
179    
180            /** _more_ */
181            public static StormParam PARAM_MOVESPEED;
182    
183            /** _more_ */
184            private static float MISSING = 9999.0f;
185    
186            /** _more_ */
187            private static final String ZEROHOUR = "0";
188    
189            /** _more_ */
190            private static final String TABLE_TRACK = "typhoon";
191    
192            /** _more_ */
193            private static final String COL_TYPHOON_YEAR = "year";
194    
195            /** _more_ */
196            private static final String COL_TYPHOON_HOUR = "hour";
197            /**/
198    
199            /** _more_ */
200            private static final String COL_TYPHOON_STORMID = "nno";
201    
202            /** _more_ */
203            private static final String COL_TYPHOON_TIME = "time";
204    
205            /** _more_ */
206            private static final String COL_TYPHOON_LATITUDE = "lat";
207    
208            /** _more_ */
209            private static final String COL_TYPHOON_LONGITUDE = "lon";
210    
211            /** _more_ */
212            private static final String COL_TYPHOON_MONTH = "mon";
213    
214            /** _more_ */
215            private static final String COL_TYPHOON_DAY = "day";
216    
217            /** _more_ */
218            private static final String COL_TYPHOON_FHOUR = "fhour";
219    
220            /** _more_ */
221            private static final String COL_TYPHOON_WAY = "way";
222    
223            /** _more_ */
224            private static final String COL_TYPHOON_PRESSURE = "pressure";
225    
226            /** _more_ */
227            private static final String COL_TYPHOON_WINDSPEED = "wind";
228    
229            /** _more_ */
230            private static final String COL_TYPHOON_RADIUSMG = "xx1";
231    
232            /** _more_ */
233            private static final String COL_TYPHOON_RADIUSWG = "xx2";
234    
235            /** _more_ */
236            private static final String COL_TYPHOON_MOVEDIR = "xx3";
237    
238            /** _more_ */
239            private static final String COL_TYPHOON_MOVESPEED = "xx4";
240    
241            /** _more_ */
242            private static final String TABLE_PROBILITY = "probility";
243    
244            /** _more_ */
245            private static final String COL_PROBILITY_WAYNAME = "wayname";
246    
247            /** _more_ */
248            private static final String COL_PROBILITY_FHOUR = "fhour";
249    
250            /** _more_ */
251            private static final String COL_PROBILITY_P10 = "p10";
252    
253            /** _more_ */
254            private static final String COL_PROBILITY_P20 = "p20";
255    
256            /** _more_ */
257            private static final String COL_PROBILITY_P30 = "p30";
258    
259            /** _more_ */
260            private static final String COL_PROBILITY_P40 = "p40";
261    
262            /** _more_ */
263            private static final String COL_PROBILITY_P50 = "p50";
264    
265            /** _more_ */
266            private static final String COL_PROBILITY_P60 = "p60";
267    
268            /** _more_ */
269            private static final String COL_PROBILITY_P70 = "p70";
270    
271            /** _more_ */
272            private static final String COL_PROBILITY_P80 = "p80";
273    
274            /** _more_ */
275            private static final String COL_PROBILITY_P90 = "p90";
276    
277            /** _more_ */
278            private static final String COL_PROBILITY_P100 = "p100";
279    
280            /** _more_ */
281            private static final String COL_DISTANCE_ERROR = "error";
282    
283            /** _more_ */
284            private static final String COL_PROBILITY_REMARK = "remark";
285    
286            /** _more_ */
287            private String dbUrl;
288    
289            /** the db connection */
290            private Connection connection;
291    
292            /** _more_ */
293            private String fromDate = "-1 year";
294    
295            /** _more_ */
296            private String toDate = "now";
297    
298            /** the stormInfo and track */
299            private List<StormInfo> stormInfos;
300    
301            /** _more_ */
302            private HashMap<String, float[]> wayfhourToRadius;
303    
304            /**
305             * constructor of sti storm data source
306             * 
307             * 
308             * @throws Exception
309             *             _more_
310             */
311    
312            public STIStormDataSource() throws Exception {
313            }
314    
315            /**
316             * _more_
317             * 
318             * @return _more_
319             */
320            public boolean isEditable() {
321                    return true;
322            }
323    
324            static {
325                    try {
326                            // TODO: Make sure these are the right units
327                            PARAM_MAXWINDSPEED = new StormParam(makeRealType("maxwindspeed",
328                                            "Max_Windspeed", Util.parseUnit("m/s")));
329                            PARAM_RADIUSMODERATEGALE = new StormParam(makeRealType(
330                                            "radiusmoderategale", "Radius_of_Beaufort_Scale7", DataUtil
331                                                            .parseUnit("km")));
332                            PARAM_RADIUSWHOLEGALE = new StormParam(makeRealType(
333                                            "radiuswholegale", "Radius_of_Beaufort_Scale10", DataUtil
334                                                            .parseUnit("km")));
335                            PARAM_MOVEDIRECTION = new StormParam(makeRealType("movedirection",
336                                            "Storm_Direction", CommonUnit.degree));
337                            PARAM_MOVESPEED = new StormParam(makeRealType("movespeed",
338                                            "Storm_Speed", Util.parseUnit("m/s")));
339    
340                            PARAM_PROBABILITY10RADIUS = new StormParam(makeRealType(
341                                            "probabilityradius10", "Probability_10%_Radius", DataUtil
342                                                            .parseUnit("km")), true, false, false);
343                            PARAM_PROBABILITY20RADIUS = new StormParam(makeRealType(
344                                            "probabilityradius20", "Probability_20%_Radius", DataUtil
345                                                            .parseUnit("km")), true, false, false);
346                            PARAM_PROBABILITY30RADIUS = new StormParam(makeRealType(
347                                            "probabilityradius30", "Probability_30%_Radius", DataUtil
348                                                            .parseUnit("km")), true, false, false);
349                            PARAM_PROBABILITY40RADIUS = new StormParam(makeRealType(
350                                            "probabilityradius40", "Probability_40%_Radius", DataUtil
351                                                            .parseUnit("km")), true, false, false);
352                            PARAM_PROBABILITY50RADIUS = new StormParam(makeRealType(
353                                            "probabilityradius50", "Probability_50%_Radius", DataUtil
354                                                            .parseUnit("km")), true, false, false);
355                            PARAM_PROBABILITY60RADIUS = new StormParam(makeRealType(
356                                            "probabilityradius60", "Probability_60%_Radius", DataUtil
357                                                            .parseUnit("km")), true, false, false);
358                            PARAM_PROBABILITY70RADIUS = new StormParam(makeRealType(
359                                            "probabilityradius70", "Probability_70%_Radius", DataUtil
360                                                            .parseUnit("km")), true, false, false);
361                            PARAM_PROBABILITY80RADIUS = new StormParam(makeRealType(
362                                            "probabilityradius80", "Probability_80%_Radius", DataUtil
363                                                            .parseUnit("km")), true, false, false);
364                            PARAM_PROBABILITY90RADIUS = new StormParam(makeRealType(
365                                            "probabilityradius90", "Probability_90%_Radius", DataUtil
366                                                            .parseUnit("km")), true, false, false);
367                            PARAM_PROBABILITY100RADIUS = new StormParam(makeRealType(
368                                            "probabilityradius100", "Probability_100%_Radius", DataUtil
369                                                            .parseUnit("km")), true, false, false);
370                            PARAM_DISTANCE_ERROR = new StormParam(makeRealType(
371                                            "meanDistanceError", "Mean_Distance_Error", DataUtil
372                                                            .parseUnit("km")), true, false, false);
373                    } catch (Exception exc) {
374                            System.err.println("Error creating storm params:" + exc);
375                            exc.printStackTrace();
376    
377                    }
378            }
379    
380            /**
381             * _more_
382             * 
383             * @throws VisADException
384             *             _more_
385             */
386            protected void initParams() throws VisADException {
387                    super.initParams();
388    
389                    obsParams = new StormParam[] { PARAM_MAXWINDSPEED, PARAM_MINPRESSURE,
390                                    PARAM_RADIUSMODERATEGALE, PARAM_RADIUSWHOLEGALE,
391                                    PARAM_MOVEDIRECTION, PARAM_MOVESPEED };
392    
393                    forecastParams = new StormParam[] {
394                                    PARAM_MAXWINDSPEED,
395                                    PARAM_MINPRESSURE,
396                                    PARAM_RADIUSMODERATEGALE,
397                                    PARAM_RADIUSWHOLEGALE,
398                                    PARAM_MOVEDIRECTION,
399                                    PARAM_MOVESPEED, // PARAM_DISTANCEERROR,
400                                    PARAM_PROBABILITY10RADIUS, PARAM_PROBABILITY20RADIUS,
401                                    PARAM_PROBABILITY30RADIUS, PARAM_PROBABILITY40RADIUS,
402                                    PARAM_PROBABILITY50RADIUS, PARAM_PROBABILITY60RADIUS,
403                                    PARAM_PROBABILITY70RADIUS, PARAM_PROBABILITY80RADIUS,
404                                    PARAM_PROBABILITY90RADIUS, PARAM_PROBABILITY100RADIUS,
405                                    PARAM_DISTANCE_ERROR };
406            }
407    
408            /**
409             * _more_
410             * 
411             * @param descriptor
412             *            _more_
413             * @param url
414             *            _more_
415             * @param properties
416             *            _more_
417             * 
418             * @throws Exception
419             *             _more_
420             */
421            public STIStormDataSource(DataSourceDescriptor descriptor, String url,
422                            Hashtable properties) throws Exception {
423                    super(descriptor, "STI Storm Data", "STI Storm Data", properties);
424                    if ((url != null) && url.trim().equals("test")) {
425                            url = DEFAULT_DERBY_URL;
426                    }
427                    if ((url == null) || url.trim().equalsIgnoreCase("default")
428                                    || (url.trim().length() == 0)) {
429                            url = (useDerby() ? DEFAULT_DERBY_URL : DEFAULT_URL);
430                    }
431                    dbUrl = url;
432            }
433    
434            /**
435             * _more_
436             */
437            protected void initializeStormData() {
438                    try {
439                            initParams();
440                            File userDir = getDataContext().getIdv().getObjectStore()
441                                            .getUserDirectory();
442                            String derbyDir = IOUtil.joinDir(userDir, "derbydb");
443                            IOUtil.makeDirRecursive(new File(derbyDir));
444                            System.setProperty("derby.system.home", derbyDir);
445    
446                            Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
447                            Class.forName("com.mysql.jdbc.Driver");
448                            if (!initConnection()) {
449                                    setInError(true, true,
450                                                    "Unable to initialize database connection:" + dbUrl);
451                            } else {
452                                    stormInfos = getAllStormInfos();
453                            }
454                    } catch (Exception exc) {
455                            logException("Error initializing STI database: " + dbUrl, exc);
456                    }
457            }
458    
459            /**
460             * _more_
461             * 
462             * @return _more_
463             */
464            public List<StormInfo> getStormInfos() {
465                    List<StormInfo> sInfos = new ArrayList();
466                    sInfos.addAll(stormInfos);
467                    return sInfos;
468            }
469    
470            /**
471             * _more_
472             * 
473             * @param stormInfo
474             *            _more_
475             * @param waysToUse
476             *            _more_
477             * @param observationWay
478             *            _more_
479             * 
480             * @return _more_
481             * 
482             * @throws Exception
483             *             _more_
484             */
485    
486            public StormTrackCollection getTrackCollectionInner(StormInfo stormInfo,
487                            Hashtable<String, Boolean> waysToUse, Way observationWay)
488                            throws Exception {
489                    if (observationWay == null) {
490                            observationWay = DEFAULT_OBSERVATION_WAY;
491                    }
492    
493                    long t1 = System.currentTimeMillis();
494                    StormTrackCollection trackCollection = new StormTrackCollection();
495                    List<Way> forecastWays = getForecastWays(stormInfo);
496    
497                    getWayProbabilityRadius();
498                    for (Way forecastWay : forecastWays) {
499                            if ((waysToUse != null) && (waysToUse.size() > 0)
500                                            && (waysToUse.get(forecastWay.getId()) == null)) {
501                                    continue;
502                            }
503                            List forecastTracks = getForecastTracks(stormInfo, forecastWay);
504                            if (forecastTracks.size() > 0) {
505                                    trackCollection.addTrackList(forecastTracks);
506                            }
507                    }
508                    StormTrack obsTrack = getObservationTrack(stormInfo, observationWay);
509                    // (Way) forecastWays.get(0));
510                    if (obsTrack != null) {
511                            List<StormTrack> tracks = trackCollection.getTracks();
512                            // for (StormTrack stk : tracks) {
513                            // addDistanceError(obsTrack, stk);
514                            // }
515                            long t2 = System.currentTimeMillis();
516                            // System.err.println("time:" + (t2 - t1));
517                            trackCollection.addTrack(obsTrack);
518                    }
519                    return trackCollection;
520            }
521    
522            /**
523             * _more_
524             * 
525             * 
526             * 
527             * @param stormInfo
528             *            _more_
529             * @param forecastWay
530             *            _more_
531             * 
532             * @return _more_
533             * @throws Exception
534             *             _more_
535             */
536            private List<StormTrack> getForecastTracks(StormInfo stormInfo,
537                            Way forecastWay) throws Exception {
538    
539                    List<StormTrack> tracks = new ArrayList<StormTrack>();
540                    List<DateTime> startDates = getForecastTrackStartDates(stormInfo,
541                                    forecastWay);
542    
543                    int nstarts = startDates.size();
544                    for (int i = 0; i < nstarts; i++) {
545                            DateTime dt = (DateTime) startDates.get(i);
546                            StormTrack tk = getForecastTrack(stormInfo, dt, forecastWay);
547                            if (tk != null) {
548                                    int pn = tk.getTrackPoints().size();
549                                    // Why > 1???
550                                    if (pn > 1) {
551                                            tracks.add(tk);
552                                    }
553                            }
554                    }
555                    return tracks;
556    
557            }
558    
559            /**
560             * If d is a missing value return NaN. Else return d
561             * 
562             * @param d
563             *            is checked if not missing return same value
564             * @param name
565             *            _more_
566             * 
567             * @return _more_
568             */
569    
570            public double getValue(double d, String name) {
571                    if ((d == 9999) || (d == 999)) {
572                            return Double.NaN;
573                    }
574    
575                    if (name.equalsIgnoreCase(PARAM_MAXWINDSPEED.getName())) {
576                            if ((d < 0) || (d > 60)) {
577                                    return Double.NaN;
578                            }
579                    } else if (name.equalsIgnoreCase(PARAM_MINPRESSURE.getName())) {
580                            if ((d < 800) || (d > 1050)) {
581                                    return Double.NaN;
582                            }
583                    } else if (name.equalsIgnoreCase(PARAM_RADIUSMODERATEGALE.getName())) {
584                            if ((d < 0) || (d > 900)) {
585                                    return Double.NaN;
586                            }
587                    } else if (name.equalsIgnoreCase(PARAM_RADIUSWHOLEGALE.getName())) {
588                            if ((d < 0) || (d > 500)) {
589                                    return Double.NaN;
590                            }
591                    } else if (name.equalsIgnoreCase(PARAM_MOVESPEED.getName())) {
592                            if ((d < 0) || (d > 55)) {
593                                    return Double.NaN;
594                            }
595                    } else if (name.equalsIgnoreCase(PARAM_MOVEDIRECTION.getName())) {
596                            if ((d < 0) || (d > 360)) {
597                                    return Double.NaN;
598                            }
599                    }
600    
601                    return d;
602            }
603    
604            /**
605             * _more_
606             * 
607             * @param d
608             *            _more_
609             * 
610             * @return _more_
611             */
612            public double getLatLonValue(double d) {
613                    if ((d == 9999) || (d == 999)) {
614                            return Double.NaN;
615                    }
616                    return d;
617            }
618    
619            /**
620             * _more_
621             * 
622             * 
623             * 
624             * @param stormInfo
625             *            _more_
626             * @param sTime
627             *            _more_
628             * @param forecastWay
629             *            _more_
630             * 
631             * @return _more_
632             * @throws Exception
633             *             _more_
634             */
635            private StormTrack getForecastTrack(StormInfo stormInfo, DateTime sTime,
636                            Way forecastWay) throws Exception {
637    
638                    // if(true) return getForecastTrackX(stormInfo, sTime, forecastWay);
639                    String columns = SqlUtil.comma(new String[] { getColYear(),
640                                    COL_TYPHOON_MONTH, COL_TYPHOON_DAY, getColHour(),
641                                    COL_TYPHOON_FHOUR, COL_TYPHOON_LATITUDE, COL_TYPHOON_LONGITUDE,
642                                    COL_TYPHOON_WINDSPEED, COL_TYPHOON_PRESSURE,
643                                    COL_TYPHOON_RADIUSMG, COL_TYPHOON_RADIUSWG,
644                                    COL_TYPHOON_MOVEDIR, COL_TYPHOON_MOVESPEED });
645    
646                    List whereList = new ArrayList();
647                    whereList.add(SqlUtil.eq(COL_TYPHOON_STORMID, SqlUtil.quote(stormInfo
648                                    .getStormId())));
649                    whereList.add(SqlUtil.eq(COL_TYPHOON_WAY, SqlUtil.quote(forecastWay
650                                    .getId())));
651    
652                    addDateSelection(sTime, whereList);
653    
654                    String query = SqlUtil.makeSelect(columns, Misc.newList(TABLE_TRACK),
655                                    SqlUtil.makeAnd(whereList));
656                    query = query
657                                    + " order by  "
658                                    + SqlUtil.comma(new String[] { getColYear(), COL_TYPHOON_MONTH,
659                                                    COL_TYPHOON_DAY, getColHour(), COL_TYPHOON_FHOUR });
660                    // System.err.println (query);
661                    Statement statement = evaluate(query);
662                    SqlUtil.Iterator iter = SqlUtil.getIterator(statement);
663                    ResultSet results;
664                    double radius = 0;
665                    List<StormTrackPoint> pts = new ArrayList();
666    
667                    Real altReal = new Real(RealType.Altitude, 0);
668                    while ((results = iter.getNext()) != null) {
669                            // System.err.println ("row " + cnt);
670                            List<Real> attrs = new ArrayList<Real>();
671                            int col = 1;
672                            int year = results.getInt(col++);
673                            int month = results.getInt(col++);
674                            int day = results.getInt(col++);
675                            int hour = results.getInt(col++);
676                            int fhour = results.getInt(col++);
677    
678                            double latitude = getLatLonValue(results.getDouble(col++));
679                            if ((latitude > 90) || (latitude < -90)) {
680                                    continue;
681                            }
682                            double longitude = getLatLonValue(results.getDouble(col++));
683                            if ((longitude > 360) || (longitude < -180)) {
684                                    continue;
685                            }
686                            attrs.add(PARAM_MAXWINDSPEED.getReal(getValue(results
687                                            .getDouble(col++), PARAM_MAXWINDSPEED.getName())));
688                            attrs.add(PARAM_MINPRESSURE.getReal(getValue(results
689                                            .getDouble(col++), PARAM_MINPRESSURE.getName())));
690                            attrs.add(PARAM_RADIUSMODERATEGALE.getReal(getValue(results
691                                            .getDouble(col++), PARAM_RADIUSMODERATEGALE.getName())));
692                            attrs.add(PARAM_RADIUSWHOLEGALE.getReal(getValue(results
693                                            .getDouble(col++), PARAM_RADIUSWHOLEGALE.getName())));
694                            attrs.add(PARAM_MOVEDIRECTION.getReal(getValue(results
695                                            .getDouble(col++), PARAM_MOVEDIRECTION.getName())));
696                            attrs.add(PARAM_MOVESPEED.getReal(getValue(
697                                            results.getDouble(col++), PARAM_MOVESPEED.getName())));
698                            float[] radiuses = getProbabilityRadius(forecastWay, fhour);
699                            DateTime dttm = getDateTime(year, month, day, hour + fhour);
700                            EarthLocation elt = new EarthLocationLite(new Real(
701                                            RealType.Latitude, latitude), new Real(RealType.Longitude,
702                                            longitude), altReal);
703                            if (true) { // radiuses != null) {
704                                    // radius = fhour * 50.0f / 24.0f;
705                                    addProbabilityRadiusAttrs(attrs, radiuses);
706                            }
707                            StormTrackPoint stp = new StormTrackPoint(elt, dttm, fhour, attrs);
708                            if (!elt.isMissing()) {
709                                    pts.add(stp);
710                            }
711                    }
712    
713                    if (pts.size() == 0) {
714                            // We should never be here
715                            System.err.println("found no track data time=" + sTime
716                                            + " from query:" + SqlUtil.makeAnd(whereList));
717                    }
718                    if (pts.size() > 0) {
719                            return new StormTrack(stormInfo, forecastWay, pts, forecastParams);
720                    } else {
721                            return null;
722                    }
723    
724            }
725    
726            /**
727             * _more_
728             * 
729             * @param way
730             *            _more_
731             * @param forecastHour
732             *            _more_
733             * 
734             * @return _more_
735             */
736            private float[] getProbabilityRadius(Way way, int forecastHour) {
737                    String key = way.getId().toUpperCase() + forecastHour;
738                    // System.out.println("get:" + key + " "
739                    // +(wayfhourToRadius.get(key)!=null));
740                    return wayfhourToRadius.get(key);
741            }
742    
743            /**
744             * _more_
745             * 
746             * @param way
747             *            _more_
748             * @param forecastHour
749             *            _more_
750             * @param radiuses
751             *            _more_
752             */
753            private void putProbabilityRadius(Way way, int forecastHour,
754                            float[] radiuses) {
755                    String key = way.getId().toUpperCase() + forecastHour;
756                    // System.out.println("put:" + key);
757                    wayfhourToRadius.put(key, radiuses);
758            }
759    
760            /**
761             * _more_
762             * 
763             * @param attrs
764             *            _more_
765             * @param radiuses
766             *            _more_
767             * 
768             * @throws Exception
769             *             _more_
770             */
771            private void addProbabilityRadiusAttrs(List<Real> attrs, float[] radiuses)
772                            throws Exception {
773                    if (radiuses != null) {
774                            attrs.add(PARAM_PROBABILITY10RADIUS.getReal(radiuses[0]));
775                            attrs.add(PARAM_PROBABILITY20RADIUS.getReal(radiuses[1]));
776                            attrs.add(PARAM_PROBABILITY30RADIUS.getReal(radiuses[2]));
777                            attrs.add(PARAM_PROBABILITY40RADIUS.getReal(radiuses[3]));
778                            attrs.add(PARAM_PROBABILITY50RADIUS.getReal(radiuses[4]));
779                            attrs.add(PARAM_PROBABILITY60RADIUS.getReal(radiuses[5]));
780                            attrs.add(PARAM_PROBABILITY70RADIUS.getReal(radiuses[6]));
781                            attrs.add(PARAM_PROBABILITY80RADIUS.getReal(radiuses[7]));
782                            attrs.add(PARAM_PROBABILITY90RADIUS.getReal(radiuses[8]));
783                            attrs.add(PARAM_PROBABILITY100RADIUS.getReal(radiuses[9]));
784                            attrs.add(PARAM_DISTANCE_ERROR
785                                            .getReal(getLatLonValue(radiuses[10])));
786                    } else {
787                            attrs.add(PARAM_PROBABILITY10RADIUS.getReal(Float.NaN));
788                            attrs.add(PARAM_PROBABILITY20RADIUS.getReal(Float.NaN));
789                            attrs.add(PARAM_PROBABILITY30RADIUS.getReal(Float.NaN));
790                            attrs.add(PARAM_PROBABILITY40RADIUS.getReal(Float.NaN));
791                            attrs.add(PARAM_PROBABILITY50RADIUS.getReal(Float.NaN));
792                            attrs.add(PARAM_PROBABILITY60RADIUS.getReal(Float.NaN));
793                            attrs.add(PARAM_PROBABILITY70RADIUS.getReal(Float.NaN));
794                            attrs.add(PARAM_PROBABILITY80RADIUS.getReal(Float.NaN));
795                            attrs.add(PARAM_PROBABILITY90RADIUS.getReal(Float.NaN));
796                            attrs.add(PARAM_PROBABILITY100RADIUS.getReal(Float.NaN));
797                            attrs.add(PARAM_DISTANCE_ERROR.getReal(Float.NaN));
798    
799                    }
800            }
801    
802            /**
803             * _more_
804             * 
805             * @param sTime
806             *            _more_
807             * @param whereList
808             *            _more_
809             * 
810             * @throws VisADException
811             *             _more_
812             */
813            private void addDateSelection(DateTime sTime, List whereList)
814                            throws VisADException {
815                    GregorianCalendar cal = new GregorianCalendar(DateUtil.TIMEZONE_GMT);
816                    cal.setTime(ucar.visad.Util.makeDate(sTime));
817                    int yy = cal.get(Calendar.YEAR);
818                    // The MONTH is 0 based. The db month is 1 based
819                    int mm = cal.get(Calendar.MONTH) + 1;
820                    int dd = cal.get(Calendar.DAY_OF_MONTH);
821                    int hh = cal.get(Calendar.HOUR_OF_DAY);
822                    whereList.add(SqlUtil.eq(getColYear(), Integer.toString(yy)));
823                    whereList.add(SqlUtil.eq(COL_TYPHOON_MONTH, Integer.toString(mm)));
824                    whereList.add(SqlUtil.eq(COL_TYPHOON_DAY, Integer.toString(dd)));
825                    whereList.add(SqlUtil.eq(getColHour(), Integer.toString(hh)));
826            }
827    
828            /**
829             * _more_
830             * 
831             * @param year
832             *            _more_
833             * @param month
834             *            _more_
835             * @param day
836             *            _more_
837             * @param hour
838             *            _more_
839             * 
840             * @return _more_
841             * 
842             * @throws Exception
843             *             _more_
844             */
845            private DateTime getDateTime(int year, int month, int day, int hour)
846                            throws Exception {
847                    GregorianCalendar convertCal = new GregorianCalendar(
848                                    DateUtil.TIMEZONE_GMT);
849                    convertCal.clear();
850                    convertCal.set(Calendar.YEAR, year);
851                    // The MONTH is 0 based. The incoming month is 1 based
852                    convertCal.set(Calendar.MONTH, month - 1);
853                    convertCal.set(Calendar.DAY_OF_MONTH, day);
854                    convertCal.set(Calendar.HOUR_OF_DAY, hour);
855                    return new DateTime(convertCal.getTime());
856            }
857    
858            /**
859             * _more_
860             * 
861             * 
862             * 
863             * @param stormInfo
864             *            _more_
865             * @param way
866             *            _more_
867             * 
868             * @return _more_
869             * @throws Exception
870             *             _more_
871             */
872            protected List<DateTime> getForecastTrackStartDates(StormInfo stormInfo,
873                            Way way) throws Exception {
874    
875                    String columns = SqlUtil.comma(new String[] { getColYear(),
876                                    COL_TYPHOON_MONTH, COL_TYPHOON_DAY, getColHour() });
877    
878                    List whereList = new ArrayList();
879                    whereList.add(SqlUtil.eq(COL_TYPHOON_STORMID, SqlUtil.quote(stormInfo
880                                    .getStormId())));
881                    whereList.add(SqlUtil.eq(COL_TYPHOON_FHOUR, ZEROHOUR));
882                    whereList.add(SqlUtil.eq(COL_TYPHOON_WAY, SqlUtil.quote(way.getId())));
883    
884                    String query = SqlUtil.makeSelect(columns, Misc.newList(TABLE_TRACK),
885                                    SqlUtil.makeAnd(whereList));
886                    query = query
887                                    + " order by  "
888                                    + SqlUtil.comma(new String[] { getColYear(), COL_TYPHOON_MONTH,
889                                                    COL_TYPHOON_DAY, getColHour() });
890                    // System.err.println (query);
891                    Statement statement = evaluate(query);
892                    SqlUtil.Iterator iter = SqlUtil.getIterator(statement);
893                    ResultSet results;
894                    List<DateTime> startDates = new ArrayList<DateTime>();
895                    while ((results = iter.getNext()) != null) {
896                            int col = 1;
897                            int year = results.getInt(col++);
898                            int month = results.getInt(col++);
899                            int day = results.getInt(col++);
900                            int hour = results.getInt(col++);
901                            startDates.add(getDateTime(year, month, day, hour));
902                    }
903                    return startDates;
904            }
905    
906            /**
907             * _more_
908             * 
909             * 
910             * @throws Exception
911             *             _more_
912             */
913            protected void getWayProbabilityRadius() throws Exception {
914    
915                    String columns = SqlUtil.comma(new String[] { COL_PROBILITY_WAYNAME,
916                                    COL_PROBILITY_FHOUR, COL_PROBILITY_P10, COL_PROBILITY_P20,
917                                    COL_PROBILITY_P30, COL_PROBILITY_P40, COL_PROBILITY_P50,
918                                    COL_PROBILITY_P60, COL_PROBILITY_P70, COL_PROBILITY_P80,
919                                    COL_PROBILITY_P90, COL_PROBILITY_P100, COL_DISTANCE_ERROR });
920    
921                    List whereList = new ArrayList();
922    
923                    String query = SqlUtil.makeSelect(columns, Misc
924                                    .newList(TABLE_PROBILITY), SqlUtil.makeAnd(whereList));
925                    Statement statement = evaluate(query);
926                    SqlUtil.Iterator iter = SqlUtil.getIterator(statement);
927                    ResultSet results;
928                    wayfhourToRadius = new HashMap();
929                    while ((results = iter.getNext()) != null) {
930                            float[] wp = new float[11];
931                            int col = 1;
932                            String wayName = results.getString(col++);
933                            int fhour = results.getInt(col++);
934                            wp[0] = results.getFloat(col++);
935                            wp[1] = results.getFloat(col++);
936                            wp[2] = results.getFloat(col++);
937                            wp[3] = results.getFloat(col++);
938                            wp[4] = results.getFloat(col++);
939                            wp[5] = results.getFloat(col++);
940                            wp[6] = results.getFloat(col++);
941                            wp[7] = results.getFloat(col++);
942                            wp[8] = results.getFloat(col++);
943                            wp[9] = results.getFloat(col++);
944                            wp[10] = results.getFloat(col++);
945                            putProbabilityRadius(new Way(wayName), fhour, wp);
946                    }
947            }
948    
949            /**
950             * _more_
951             * 
952             * @param stormInfo
953             *            _more_
954             * @param observationWay
955             *            _more_
956             * 
957             * @return _more_
958             * 
959             * @throws Exception
960             *             _more_
961             */
962            protected StormTrack getObservationTrack(StormInfo stormInfo,
963                            Way observationWay) throws Exception {
964                    addWay(observationWay);
965                    // first get the obs from one specific way
966                    List<StormTrackPoint> obsTrackPoints = getObservationTrackPoints(
967                                    stormInfo, observationWay);
968    
969                    if (obsTrackPoints.size() == 0) {
970                            return null;
971                    }
972    
973                    return new StormTrack(stormInfo, addWay(Way.OBSERVATION),
974                                    obsTrackPoints, obsParams);
975            }
976    
977            /**
978             * _more_
979             * 
980             * @return _more_
981             */
982            public boolean getIsObservationWayChangeable() {
983                    return true;
984            }
985    
986            /**
987             * _more_
988             * 
989             * @return _more_
990             */
991            public Way getDefaultObservationWay() {
992                    return DEFAULT_OBSERVATION_WAY;
993            }
994    
995            /**
996             * _more_
997             * 
998             * 
999             * 
1000             * @param stormInfo
1001             *            _more_
1002             * @param wy
1003             *            _more_
1004             * 
1005             * @return _more_
1006             * @throws Exception
1007             *             _more_
1008             */
1009            protected List<StormTrackPoint> getObservationTrackPoints(
1010                            StormInfo stormInfo, Way wy) throws Exception {
1011                    String columns = SqlUtil.comma(new String[] { getColYear(),
1012                                    COL_TYPHOON_MONTH, COL_TYPHOON_DAY, getColHour(),
1013                                    COL_TYPHOON_LATITUDE, COL_TYPHOON_LONGITUDE,
1014                                    COL_TYPHOON_WINDSPEED, COL_TYPHOON_PRESSURE,
1015                                    COL_TYPHOON_RADIUSMG, COL_TYPHOON_RADIUSWG,
1016                                    COL_TYPHOON_MOVEDIR, COL_TYPHOON_MOVESPEED, COL_TYPHOON_WAY });
1017    
1018                    List whereList = new ArrayList();
1019    
1020                    whereList.add(SqlUtil.eq(COL_TYPHOON_STORMID, SqlUtil.quote(stormInfo
1021                                    .getStormId())));
1022                    whereList.add(SqlUtil.eq(COL_TYPHOON_FHOUR, ZEROHOUR));
1023                    whereList.add(SqlUtil.eq(COL_TYPHOON_WAY, SqlUtil.quote(wy.getId())));
1024    
1025                    String query = SqlUtil.makeSelect(columns, Misc.newList(TABLE_TRACK),
1026                                    SqlUtil.makeAnd(whereList));
1027                    query = query
1028                                    + " order by  "
1029                                    + SqlUtil.comma(new String[] { getColYear(), COL_TYPHOON_MONTH,
1030                                                    COL_TYPHOON_DAY, getColHour() });
1031                    // System.err.println (query);
1032                    Statement statement = evaluate(query);
1033                    SqlUtil.Iterator iter = SqlUtil.getIterator(statement);
1034                    ResultSet results;
1035    
1036                    List<StormTrackPoint> obsPts = new ArrayList();
1037                    // Hashtable seenDate = new Hashtable();
1038                    Real altReal = new Real(RealType.Altitude, 0);
1039    
1040                    while ((results = iter.getNext()) != null) {
1041                            List<Real> attrs = new ArrayList();
1042                            int col = 1;
1043                            int year = results.getInt(col++);
1044                            int month = results.getInt(col++);
1045                            int day = results.getInt(col++);
1046                            int hour = results.getInt(col++);
1047                            double latitude = getLatLonValue(results.getDouble(col++));
1048                            if ((latitude > 90) || (latitude < -90)) {
1049                                    continue;
1050                            }
1051                            double longitude = getLatLonValue(results.getDouble(col++));
1052                            if ((longitude > 360) || (longitude < -180)) {
1053                                    continue;
1054                            }
1055                            attrs.add(PARAM_MAXWINDSPEED.getReal(getValue(results
1056                                            .getDouble(col++), PARAM_MAXWINDSPEED.getName())));
1057                            attrs.add(PARAM_MINPRESSURE.getReal(getValue(results
1058                                            .getDouble(col++), PARAM_MINPRESSURE.getName())));
1059                            attrs.add(PARAM_RADIUSMODERATEGALE.getReal(getValue(results
1060                                            .getDouble(col++), PARAM_RADIUSMODERATEGALE.getName())));
1061                            attrs.add(PARAM_RADIUSWHOLEGALE.getReal(getValue(results
1062                                            .getDouble(col++), PARAM_RADIUSWHOLEGALE.getName())));
1063                            attrs.add(PARAM_MOVEDIRECTION.getReal(getValue(results
1064                                            .getDouble(col++), PARAM_MOVEDIRECTION.getName())));
1065                            attrs.add(PARAM_MOVESPEED.getReal(getValue(
1066                                            results.getDouble(col++), PARAM_MOVESPEED.getName())));
1067    
1068                            EarthLocation elt = new EarthLocationLite(new Real(
1069                                            RealType.Latitude, latitude), new Real(RealType.Longitude,
1070                                            longitude), altReal);
1071    
1072                            DateTime date = getDateTime(year, month, day, hour);
1073                            String key = "" + latitude + " " + longitude;
1074                            // if(seenDate.get(date)!=null) {
1075                            // if(!seenDate.get(date).equals(key)) {
1076                            // System.err.println ("seen: " + date + " " + seenDate.get(date) +
1077                            // " != " + key);
1078                            // }
1079                            // continue;
1080                            // }
1081                            // seenDate.put(date,date);
1082                            // seenDate.put(date,key);
1083                            StormTrackPoint stp = new StormTrackPoint(elt, date, 0, attrs);
1084                            obsPts.add(stp);
1085                    }
1086    
1087                    return obsPts;
1088            }
1089    
1090            /**
1091             * _more_
1092             * 
1093             * @param stormInfo
1094             *            _more_
1095             * @param wy
1096             *            _more_
1097             * @param before
1098             *            _more_
1099             * @param after
1100             *            _more_
1101             * @param pts
1102             *            _more_
1103             * 
1104             * @return _more_
1105             * 
1106             * @throws Exception
1107             *             _more_
1108             */
1109            protected List<StormTrackPoint> getObservationTrack(StormInfo stormInfo,
1110                            Way wy, DateTime before, DateTime after, List pts) throws Exception {
1111    
1112                    String columns = SqlUtil.comma(new String[] { getColYear(),
1113                                    COL_TYPHOON_MONTH, COL_TYPHOON_DAY, getColHour(),
1114                                    COL_TYPHOON_LATITUDE, COL_TYPHOON_LONGITUDE,
1115                                    COL_TYPHOON_WINDSPEED, COL_TYPHOON_PRESSURE,
1116                                    COL_TYPHOON_RADIUSMG, COL_TYPHOON_RADIUSWG,
1117                                    COL_TYPHOON_MOVEDIR, COL_TYPHOON_MOVESPEED, COL_TYPHOON_WAY });
1118    
1119                    List whereList = new ArrayList();
1120    
1121                    whereList.add(SqlUtil.eq(COL_TYPHOON_STORMID, SqlUtil.quote(stormInfo
1122                                    .getStormId())));
1123                    whereList.add(SqlUtil.eq(COL_TYPHOON_FHOUR, ZEROHOUR));
1124                    whereList.add(SqlUtil.eq(COL_TYPHOON_WAY, SqlUtil.quote(wy.getId())));
1125    
1126                    String query = SqlUtil.makeSelect(columns, Misc.newList(TABLE_TRACK),
1127                                    SqlUtil.makeAnd(whereList));
1128                    query = query
1129                                    + " order by  "
1130                                    + SqlUtil.comma(new String[] { getColYear(), COL_TYPHOON_MONTH,
1131                                                    COL_TYPHOON_DAY, getColHour() });
1132                    // System.err.println (query);
1133                    Statement statement = evaluate(query);
1134                    SqlUtil.Iterator iter = SqlUtil.getIterator(statement);
1135                    ResultSet results;
1136    
1137                    List<StormTrackPoint> obsPts = new ArrayList();
1138                    List<StormTrackPoint> obsPts1 = new ArrayList();
1139                    List<StormTrackPoint> obsPts2 = new ArrayList();
1140                    Real altReal = new Real(RealType.Altitude, 0);
1141    
1142                    while ((results = iter.getNext()) != null) {
1143                            List<Real> attrs = new ArrayList();
1144                            int col = 1;
1145                            int year = results.getInt(col++);
1146                            int month = results.getInt(col++);
1147                            int day = results.getInt(col++);
1148                            int hour = results.getInt(col++);
1149                            double latitude = getLatLonValue(results.getDouble(col++));
1150                            if ((latitude > 90) || (latitude < -90)) {
1151                                    continue;
1152                            }
1153                            double longitude = getLatLonValue(results.getDouble(col++));
1154                            if ((longitude > 360) || (longitude < -180)) {
1155                                    continue;
1156                            }
1157    
1158                            attrs.add(PARAM_MAXWINDSPEED.getReal(getValue(results
1159                                            .getDouble(col++), PARAM_MAXWINDSPEED.getName())));
1160                            attrs.add(PARAM_MINPRESSURE.getReal(getValue(results
1161                                            .getDouble(col++), PARAM_MINPRESSURE.getName())));
1162                            attrs.add(PARAM_RADIUSMODERATEGALE.getReal(getValue(results
1163                                            .getDouble(col++), PARAM_RADIUSMODERATEGALE.getName())));
1164                            attrs.add(PARAM_RADIUSWHOLEGALE.getReal(getValue(results
1165                                            .getDouble(col++), PARAM_RADIUSWHOLEGALE.getName())));
1166                            attrs.add(PARAM_MOVEDIRECTION.getReal(getValue(results
1167                                            .getDouble(col++), PARAM_MOVEDIRECTION.getName())));
1168                            attrs.add(PARAM_MOVESPEED.getReal(getValue(
1169                                            results.getDouble(col++), PARAM_MOVESPEED.getName())));
1170    
1171                            EarthLocation elt = new EarthLocationLite(new Real(
1172                                            RealType.Latitude, latitude), new Real(RealType.Longitude,
1173                                            longitude), altReal);
1174    
1175                            DateTime date = getDateTime(year, month, day, hour);
1176    
1177                            if (date.getValue() < before.getValue()) {
1178                                    StormTrackPoint stp = new StormTrackPoint(elt, date, 0, attrs);
1179                                    obsPts1.add(stp);
1180                            }
1181    
1182                            if (date.getValue() > after.getValue()) {
1183                                    StormTrackPoint stp = new StormTrackPoint(elt, date, 0, attrs);
1184                                    obsPts2.add(stp);
1185                            }
1186    
1187                    }
1188    
1189                    if (obsPts1.size() > 0) {
1190                            obsPts.addAll(obsPts1);
1191                    }
1192    
1193                    obsPts.addAll(pts);
1194    
1195                    if (obsPts2.size() > 0) {
1196                            obsPts.addAll(obsPts2);
1197                    }
1198    
1199                    return obsPts;
1200    
1201            }
1202    
1203            /**
1204             * _more_
1205             * 
1206             * @param times
1207             *            _more_
1208             * 
1209             * @return _more_
1210             */
1211            protected DateTime getStartTime(List times) {
1212                    int size = times.size();
1213                    DateTime dt = (DateTime) times.get(0);
1214                    int idx = 0;
1215                    double value = dt.getValue();
1216                    for (int i = 1; i < size; i++) {
1217                            dt = (DateTime) times.get(i);
1218                            double dtValue = dt.getValue();
1219                            if (dtValue < value) {
1220                                    value = dtValue;
1221                                    idx = i;
1222                            }
1223                    }
1224                    return (DateTime) times.get(idx);
1225            }
1226    
1227            /**
1228             * _more_
1229             * 
1230             * 
1231             * 
1232             * @return _more_
1233             * @throws Exception
1234             *             _more_
1235             */
1236            private List<StormInfo> getAllStormInfos() throws Exception {
1237                    String columns = SqlUtil.distinct(COL_TYPHOON_STORMID);
1238                    String query = SqlUtil.makeSelect(columns, Misc.newList(TABLE_TRACK));
1239                    // System.err.println (query);
1240                    // System.err.println(query);
1241                    SqlUtil.Iterator iter = SqlUtil.getIterator(evaluate(query));
1242                    ResultSet results;
1243                    List<StormInfo> stormInfos = new ArrayList<StormInfo>();
1244                    while ((results = iter.getNext()) != null) {
1245                            String id = results.getString(1);
1246                            DateTime startTime = getStormStartTime(id);
1247                            // System.err.println(id + " " + startTime);
1248                            StormInfo sinfo = new StormInfo(id, startTime);
1249                            stormInfos.add(sinfo);
1250                    }
1251                    return stormInfos;
1252            }
1253    
1254            /**
1255             * _more_
1256             * 
1257             * @param id
1258             *            _more_
1259             * 
1260             * @return _more_
1261             * 
1262             * @throws Exception
1263             *             _more_
1264             */
1265            protected DateTime getStormStartTime(String id) throws Exception {
1266                    String columns = SqlUtil.comma(new String[] { getColYear(),
1267                                    COL_TYPHOON_MONTH, COL_TYPHOON_DAY, getColHour() });
1268    
1269                    List whereList = new ArrayList();
1270                    whereList.add(SqlUtil.eq(COL_TYPHOON_STORMID, SqlUtil.quote(id)));
1271                    whereList.add(SqlUtil.eq(COL_TYPHOON_FHOUR, ZEROHOUR));
1272                    String query = SqlUtil.makeSelect(columns, Misc.newList(TABLE_TRACK),
1273                                    SqlUtil.makeAnd(whereList));
1274                    query = query + " order by  " + columns;
1275                    // System.err.println (query);
1276                    Statement statement = evaluate(query);
1277                    SqlUtil.Iterator iter = SqlUtil.getIterator(statement);
1278                    ResultSet results;
1279                    while ((results = iter.getNext()) != null) {
1280                            int col = 1;
1281                            int year = results.getInt(col++);
1282                            int month = results.getInt(col++);
1283                            int day = results.getInt(col++);
1284                            int hour = results.getInt(col++);
1285                            statement.close();
1286                            // Just get the first one since we sorted the results with the order
1287                            // by
1288                            return getDateTime(year, month, day, hour);
1289                    }
1290                    return null;
1291            }
1292    
1293            /**
1294             * _more_
1295             * 
1296             * 
1297             * 
1298             * @param stormInfo
1299             *            _more_
1300             * 
1301             * @return _more_
1302             * @throws Exception
1303             *             _more_
1304             */
1305            protected List<Way> getForecastWays(StormInfo stormInfo) throws Exception {
1306    
1307                    String columns = SqlUtil.distinct(COL_TYPHOON_WAY);
1308    
1309                    List whereList = new ArrayList();
1310                    whereList.add(SqlUtil.eq(COL_TYPHOON_STORMID, SqlUtil.quote(stormInfo
1311                                    .getStormId())));
1312                    String query = SqlUtil.makeSelect(columns, Misc.newList(TABLE_TRACK),
1313                                    SqlUtil.makeAnd(whereList));
1314                    // System.err.println (query);
1315                    Statement statement = evaluate(query);
1316                    SqlUtil.Iterator iter = SqlUtil.getIterator(statement);
1317                    ResultSet results;
1318    
1319                    List<Way> forecastWays = new ArrayList<Way>();
1320    
1321                    // TODO: How do we handle no data???
1322                    while ((results = iter.getNext()) != null) {
1323                            Way way = new Way(results.getString(1));
1324                            addWay(way);
1325                            forecastWays.add(way);
1326                    }
1327    
1328                    // System.err.println ("ways:" + forecastWays);
1329                    return forecastWays;
1330    
1331            }
1332    
1333            /**
1334             * _more_
1335             * 
1336             * @param sql
1337             *            _more_
1338             * 
1339             * @return _more_
1340             * 
1341             * @throws SQLException
1342             *             _more_
1343             */
1344            private Statement evaluate(String sql) throws SQLException {
1345                    Statement stmt = getConnection().createStatement();
1346                    stmt.execute(sql);
1347                    return stmt;
1348            }
1349    
1350            /**
1351             * _more_
1352             * 
1353             * @return _more_
1354             */
1355            public Connection getConnection() {
1356                    if (connection != null) {
1357                            return connection;
1358                    }
1359                    // String url = getFilePath();
1360                    // Just hard code the jdbc url
1361                    String url = dbUrl;
1362                    // We don't need to do this for derby.
1363                    /*
1364                     * if ((getUserName() == null) || (getUserName().trim().length() == 0))
1365                     * { if (url.indexOf("?") >= 0) { int idx = url.indexOf("?");
1366                     * List<String> args = (List<String>) StringUtil.split(url.substring(idx
1367                     * + 1), "&", true, true); url = url.substring(0, idx); for (String tok
1368                     * : args) { List<String> subtoks = (List<String>) StringUtil.split(tok,
1369                     * "=", true, true); if (subtoks.size() != 2) { continue; } String name
1370                     * = subtoks.get(0); String value = subtoks.get(1); if
1371                     * (name.equals("user")) { setUserName(value); } else if
1372                     * (name.equals("password")) { setPassword(value); } } } }
1373                     */
1374    
1375                    int cnt = 0;
1376                    while (true) {
1377                            String userName = getUserName();
1378                            String password = getPassword();
1379                            if (userName == null) {
1380                                    userName = "";
1381                            }
1382                            if (password == null) {
1383                                    password = "";
1384                            }
1385                            // userName = "jeff";
1386                            // password = "mypassword";
1387                            try {
1388                                    // System.err.println(url);
1389                                    if (useDerby()) {
1390                                            connection = DriverManager.getConnection(url);
1391                                    } else {
1392                                            if ((url.indexOf("user") > 0)
1393                                                            && (url.indexOf("password") > 0)) {
1394                                                    connection = DriverManager.getConnection(url);
1395                                            } else {
1396                                                    connection = DriverManager.getConnection(url, userName,
1397                                                                    password);
1398                                            }
1399                                    }
1400    
1401                                    return connection;
1402                            } catch (Exception sqe) {
1403                                    // System.out.println(sqe);
1404                                    String msg = sqe.toString();
1405                                    if ((msg.indexOf("Access denied") >= 0)
1406                                                    || (msg.indexOf("role \"" + userName
1407                                                                    + "\" does not exist") >= 0)
1408                                                    || (msg.indexOf("user name specified") >= 0)) {
1409                                            String label;
1410                                            if (cnt == 0) {
1411                                                    label = "<html>The database requires a login.<br>Please enter a user name and password:</html>";
1412                                            } else {
1413                                                    label = "<html>Incorrect username/password. Please try again.</html>";
1414                                            }
1415                                            if (!showPasswordDialog("Database Login", label)) {
1416                                                    return null;
1417                                            }
1418                                            cnt++;
1419                                            continue;
1420                                    }
1421                                    throw new BadDataException("Unable to connect to database", sqe);
1422                            }
1423                    }
1424            }
1425    
1426            /**
1427             * _more_
1428             * 
1429             * @return _more_
1430             * 
1431             * @throws Exception
1432             *             _more_
1433             */
1434            private boolean initConnection() throws Exception {
1435                    if (getConnection() == null) {
1436                            return false;
1437                    }
1438    
1439                    try {
1440                            // Create the dummy database
1441                            Connection connection = getConnection();
1442                            Statement stmt = connection.createStatement();
1443                            // Drop the table - ignore any errors
1444                            // SqlUtil.loadSql("drop table " + TABLE_TRACK, stmt, false);
1445    
1446                            if (useDerby()) {
1447                                    // Load in the test data
1448                                    try {
1449                                            stmt.execute("select count(*) from typhoon");
1450                                            System.err.println("Derby DB OK");
1451                                    } catch (Exception exc) {
1452                                            System.err.println("exc;" + exc);
1453                                            System.err.println("Creating test database");
1454                                            String initSql = IOUtil.readContents(
1455                                                            "/ucar/unidata/data/storm/testdb.sql", getClass());
1456    
1457                                            connection.setAutoCommit(false);
1458                                            SqlUtil.loadSql(initSql, stmt, false);
1459                                            connection.commit();
1460                                            connection.setAutoCommit(true);
1461                                    }
1462                            }
1463                    } catch (Exception exc) {
1464                            exc.printStackTrace();
1465                            return false;
1466                    }
1467                    return true;
1468            }
1469    
1470            /**
1471             * _more_
1472             * 
1473             * @param args
1474             *            _more_
1475             * 
1476             * @throws Exception
1477             *             _more_
1478             */
1479            public static void main(String[] args) throws Exception {
1480                    String sid = "0623";
1481                    STIStormDataSource s = null;
1482                    try {
1483                            s = new STIStormDataSource();
1484                    } catch (Exception exc) {
1485                            System.err.println("err:" + exc);
1486                            exc.printStackTrace();
1487                    }
1488                    s.initAfter();
1489                    List sInfoList = s.getStormInfos();
1490                    StormInfo sInfo = (StormInfo) sInfoList.get(0);
1491                    sInfo = s.getStormInfo(sid);
1492                    String sd = sInfo.getStormId();
1493                    StormTrackCollection cls = s.getTrackCollection(sInfo, null, null);
1494                    StormTrack obsTrack = cls.getObsTrack();
1495                    List trackPointList = obsTrack.getTrackPoints();
1496                    List trackPointTime = obsTrack.getTrackTimes();
1497                    List ways = cls.getWayList();
1498                    Map mp = cls.getWayToStartDatesHashMap();
1499                    Map mp1 = cls.getWayToTracksHashMap();
1500    
1501                    System.err.println("test:");
1502    
1503            }
1504    
1505            /**
1506             * Set the DbUrl property.
1507             * 
1508             * @param value
1509             *            The new value for DbUrl
1510             */
1511            public void setDbUrl(String value) {
1512                    dbUrl = value;
1513            }
1514    
1515            /**
1516             * Get the DbUrl property.
1517             * 
1518             * @return The DbUrl
1519             */
1520            public String getDbUrl() {
1521                    return dbUrl;
1522            }
1523    
1524    }