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