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.util.ArrayList;
032import java.util.Calendar;
033import java.util.Collection;
034import java.util.Date;
035import java.util.GregorianCalendar;
036import java.util.HashMap;
037import java.util.Hashtable;
038import java.util.Iterator;
039import java.util.List;
040import java.util.Map;
041
042import org.slf4j.Logger;
043import org.slf4j.LoggerFactory;
044import ucar.unidata.data.DataSourceDescriptor;
045import ucar.unidata.data.DataUtil;
046import ucar.unidata.util.DateUtil;
047import ucar.unidata.util.IOUtil;
048import ucar.unidata.util.StringUtil;
049import ucar.visad.Util;
050import visad.CommonUnit;
051import visad.DateTime;
052import visad.Real;
053import visad.RealType;
054import visad.VisADException;
055import visad.georef.EarthLocation;
056import visad.georef.EarthLocationLite;
057
058/**
059 * Created by IntelliJ IDEA. User: yuanho Date: May 8, 2009 Time: 10:02:15 AM To
060 * change this template use File | Settings | File Templates.
061 */
062public class Diamond7StormDataSource extends StormDataSource {
063        
064        private static final Logger logger =
065                LoggerFactory.getLogger(Diamond7StormDataSource.class);
066        
067        /**
068         * _more_
069         * 
070         * @return _more_
071         */
072
073        public String getId() {
074                return "stiDiamond";
075        }
076
077        /** _more_ */
078        public static StormParam PARAM_MAXWINDSPEED;
079
080        /** _more_ */
081        public static StormParam PARAM_RADIUSMODERATEGALE;
082
083        /** _more_ */
084        public static StormParam PARAM_RADIUSWHOLEGALE;
085
086        /** _more_ */
087        public static StormParam PARAM_DISTANCE_ERROR;
088
089        /** _more_ */
090        public static StormParam PARAM_PROBABILITY100RADIUS;
091
092        /** _more_ */
093        public static StormParam PARAM_PROBABILITYRADIUS;
094
095        /** _more_ */
096        public static StormParam PARAM_MOVEDIRECTION;
097
098        /** _more_ */
099        public static StormParam PARAM_MOVESPEED;
100
101        /** _more_ */
102        private static float MISSING = 9999.0f;
103
104        /** _more_ */
105        private String fileName;
106
107        /** the stormInfo and track */
108        private List<StormInfo> stormInfos;
109
110        /** the stormInfo and track */
111        private List<StormTrack> stormTracks;
112
113        private HashMap<String, Way> stormWays;
114
115        /**
116         * constructor of sti storm data source
117         * 
118         * 
119         * 
120         * @param descriptor
121         *            _more_
122         * @param fileName
123         *            _more_
124         * @param properties
125         *            _more_
126         * @throws Exception
127         *             _more_
128         */
129
130        public Diamond7StormDataSource(DataSourceDescriptor descriptor,
131                        String fileName, Hashtable properties) throws Exception {
132                super(descriptor, fileName, "Diamond7 Storm Data", properties);
133                if ((fileName == null) || (fileName.trim().length() == 0)
134                                || fileName.trim().equalsIgnoreCase("default")) {
135                        System.err.println("No input file");
136                        ;
137                }
138
139                this.fileName = fileName;
140
141        }
142
143        /**
144         * _more_
145         * 
146         * @return _more_
147         */
148        public boolean isEditable() {
149                return true;
150        }
151
152        static {
153                try {
154                        // TODO: Make sure these are the right units
155                        PARAM_MINPRESSURE = new StormParam(makeRealType("minpressure",
156                                        "Min_Pressure", DataUtil.parseUnit("mb")));
157                        PARAM_MAXWINDSPEED = new StormParam(makeRealType("maxwindspeed",
158                                        "Max_Windspeed", Util.parseUnit("m/s")));
159                        PARAM_RADIUSMODERATEGALE = new StormParam(makeRealType(
160                                        "radiusmoderategale", "Radius_of_Beaufort_Scale7", DataUtil
161                                                        .parseUnit("km")));
162                        PARAM_RADIUSWHOLEGALE = new StormParam(makeRealType(
163                                        "radiuswholegale", "Radius_of_Beaufort_Scale10", DataUtil
164                                                        .parseUnit("km")));
165                        PARAM_MOVEDIRECTION = new StormParam(makeRealType("movedirection",
166                                        "Storm_Direction", CommonUnit.degree));
167                        PARAM_MOVESPEED = new StormParam(makeRealType("movespeed",
168                                        "Storm_Speed", Util.parseUnit("m/s")));
169                } catch (Exception exc) {
170                        logger.error("Error creating storm params", exc);
171                }
172        }
173
174        /**
175         * _more_
176         * 
177         * @throws VisADException
178         *             _more_
179         */
180        protected void initParams() throws VisADException {
181                super.initParams();
182
183                obsParams = new StormParam[] { PARAM_MAXWINDSPEED, PARAM_MINPRESSURE,
184                                PARAM_RADIUSMODERATEGALE, PARAM_RADIUSWHOLEGALE,
185                                PARAM_MOVESPEED, PARAM_MOVEDIRECTION };
186
187                forecastParams = new StormParam[] { PARAM_MAXWINDSPEED,
188                                PARAM_MINPRESSURE, PARAM_RADIUSMODERATEGALE,
189                                PARAM_RADIUSWHOLEGALE, PARAM_MOVESPEED, PARAM_MOVEDIRECTION };
190        }
191
192        /**
193         * _more_
194         * 
195         * 
196         * @throws Exception
197         *             _more_
198         */
199        public Diamond7StormDataSource() throws Exception {
200        }
201
202        /**
203         * _more_
204         */
205        protected void initializeStormData() {
206
207                try {
208                        stormInfos = new ArrayList<StormInfo>();
209                        stormTracks = new ArrayList<StormTrack>();
210                        stormWays = new HashMap<String, Way>();
211                        String s = IOUtil.readContents(fileName);
212                        /*
213                         * 
214                         * diamond 7 0807 Tropical Cyclone Track Name 0807 japn 15 08 06 15
215                         * 8 0 123.7 18.1 18.0 996.0 NaN NaN NaN NaN 08 06 15 14 6 124.4
216                         * 18.8 19.0 995.0 NaN NaN NaN NaN year mon day time forecasttime
217                         * lon lat speed pressure wind-circle-radii radii2 movspd movdir
218                         */
219                        int lcn = 0;
220                        String sid = null;
221                        String sway = null;
222                        boolean nextTrack = false;
223                        Way trackWay = null;
224                        StormInfo sInfo = null;
225                        List<StormTrackPoint> pts = null;
226                        List<StormTrackPoint> obsPts = new ArrayList();
227                        StormTrack sTrack = null;
228                        List<String> lines = StringUtil.split(s, "\n", true, true);
229                        int currentIndex = 0;
230                        String headerLine1 = lines.get(currentIndex++);
231                        double minTime = Double.MAX_VALUE;
232                        DateTime minDate = null;
233
234                        while (currentIndex < lines.size()) {
235
236                                String headerLine2 = lines.get(currentIndex++);
237                                List<String> toks = StringUtil.split(headerLine2, " ", true,
238                                                true);
239                                sid = toks.get(1);
240                                sway = toks.get(2);
241                                int numberPts = Integer.parseInt(toks.get(3));
242                                trackWay = new Way(sway);
243                                stormWays.put(sway, trackWay);
244                                if (trackWay.isObservation())
245                                        hasObservation = true;
246                                if (sInfo == null) {
247                                        sInfo = new StormInfo(sid, new DateTime(new Date()));
248                                        stormInfos.add(sInfo);
249                                }
250
251                                pts = new ArrayList();
252                                /*  */
253                                int endPtsIndex = currentIndex + numberPts;
254
255                                // System.out.println("endPtsIndex "+ endPtsIndex);
256                                while (currentIndex < endPtsIndex) {
257
258                                        // System.out.println("currentIndex "+ currentIndex);
259                                        String line = lines.get(currentIndex++);
260                                        toks = StringUtil.split(line, " ", true, true);
261                                        String year = toks.get(0);
262                                        String mon = toks.get(1);
263                                        String day = toks.get(2);
264                                        String hr = toks.get(3);
265                                        String fhr = toks.get(4);
266                                        String lon = toks.get(5);
267                                        String lat = toks.get(6);
268                                        String maxwindsp = toks.get(7);
269                                        String minpress = toks.get(8);
270                                        String radiusmgale = toks.get(9);
271                                        String radiuswgale = toks.get(10);
272                                        String mspd = toks.get(11);
273                                        String mdir = toks.get(12);
274                                        int yy = Integer.parseInt(year);
275                                        if (yy < 20)
276                                                yy = 2000 + yy;
277                                        else if (yy > 50 && yy < 99)
278                                                yy = 1900 + yy;
279                                        DateTime dtt = getDateTime(yy, Integer.parseInt(mon),
280                                                        Integer.parseInt(day), Integer.parseInt(hr));
281                                        double latitude = Double.parseDouble(lat);
282                                        double longitude = Double.parseDouble(lon);
283                                        Real altReal = new Real(RealType.Altitude, 0);
284                                        int fhour = Integer.parseInt(fhr);
285
286                                        EarthLocation elt = new EarthLocationLite(new Real(
287                                                        RealType.Latitude, latitude), new Real(
288                                                        RealType.Longitude, longitude), altReal);
289                                        List<Real> attributes = new ArrayList<Real>();
290
291                                        double windspeed = getDouble(maxwindsp);
292                                        double pressure = getDouble(minpress);
293                                        attributes.add(PARAM_MINPRESSURE.getReal(pressure));
294                                        attributes.add(PARAM_MAXWINDSPEED.getReal(windspeed));
295                                        attributes.add(PARAM_RADIUSMODERATEGALE
296                                                        .getReal(getDouble(radiusmgale)));
297                                        attributes.add(PARAM_RADIUSWHOLEGALE
298                                                        .getReal(getDouble(radiuswgale)));
299                                        attributes
300                                                        .add(PARAM_MOVEDIRECTION.getReal(getDouble(mdir)));
301                                        attributes.add(PARAM_MOVESPEED.getReal(getDouble(mspd)));
302
303                                        StormTrackPoint stp = new StormTrackPoint(elt, dtt, fhour,
304                                                        attributes);
305
306                                        // System.out.println("fhour "+ fhour);
307                                        if (fhour == 0 && !trackWay.isObservation()) {
308                                                obsPts.add(stp);
309                                        }
310                                        if (fhour == 0 && pts.size() > 0
311                                                        && !trackWay.isObservation()) {
312                                                // System.out.println("fhours "+ pts.size());
313                                                DateTime trackStartTime = pts.get(0).getTime();
314                                                if (trackStartTime.getValue() < minTime) {
315                                                        minTime = trackStartTime.getValue();
316                                                        minDate = trackStartTime;
317                                                }
318                                                sTrack = new StormTrack(sInfo, trackWay, pts,
319                                                                forecastParams);
320                                                stormTracks.add(sTrack);
321                                                pts = new ArrayList();
322                                        }
323                                        pts.add(stp);
324                                }
325                                if (trackWay.isObservation()) {
326                                        DateTime trackStartTime = pts.get(0).getTime();
327                                        if (trackStartTime.getValue() < minTime) {
328                                                minTime = trackStartTime.getValue();
329                                                minDate = trackStartTime;
330                                        }
331                                        sTrack = new StormTrack(sInfo, trackWay, pts, obsParams);
332                                        stormTracks.add(sTrack);
333                                        pts = new ArrayList();
334                                }
335                                if (pts.size() > 0 && !trackWay.isObservation()) {
336                                        DateTime trackStartTime = pts.get(0).getTime();
337                                        if (trackStartTime.getValue() < minTime) {
338                                                minTime = trackStartTime.getValue();
339                                                minDate = trackStartTime;
340                                        }
341                                        sTrack = new StormTrack(sInfo, trackWay, pts,
342                                                        forecastParams);
343                                        stormTracks.add(sTrack);
344                                        pts = new ArrayList();
345                                }
346
347                        }
348                        /* last track */
349                        if (sInfo != null && minDate != null) {
350                                sInfo.setStartTime(minDate);
351                        }
352                        /* obs */
353                        if (!hasObservation && obsPts.size() > 0) {
354                                sTrack = new StormTrack(sInfo, DEFAULT_OBSERVATION_WAY, obsPts,
355                                                obsParams);
356                                stormTracks.add(sTrack);
357                                stormWays.put("Observation", DEFAULT_OBSERVATION_WAY);
358                        }
359
360                } catch (Exception exc) {
361                        logException("Error initializing ATCF data", exc);
362                } finally {
363                        decrOutstandingGetDataCalls();
364                }
365
366        }
367
368        /**
369         * _more_
370         * 
371         * @param dstring
372         *            _more_
373         * 
374         * @return _more_
375         */
376        public double getDouble(String dstring) {
377                if (dstring.equalsIgnoreCase("NaN") || dstring.equalsIgnoreCase("9999")) {
378                        return Double.NaN;
379                } else {
380                        return Double.parseDouble(dstring);
381                }
382        }
383
384        /**
385         * _more_
386         * 
387         * @return _more_
388         */
389        public List<StormInfo> getStormInfos() {
390                List<StormInfo> sInfos = new ArrayList();
391                sInfos.addAll(stormInfos);
392                return sInfos;
393        }
394
395        /**
396         * _more_
397         * 
398         * @param stormInfo
399         *            _more_
400         * @param waysToUse
401         *            _more_
402         * @param observationWay
403         *            _more_
404         * 
405         * @return _more_
406         * 
407         * @throws Exception
408         *             _more_
409         */
410
411        /** _more_ */
412        private static final Way DEFAULT_OBSERVATION_WAY = new Way("Observation");
413        private boolean hasObservation = false;
414
415        /**
416         * _more_
417         * 
418         * @param stormInfo
419         *            _more_
420         * @param waysToUse
421         *            _more_
422         * @param observationWay
423         *            _more_
424         * 
425         * @return _more_
426         * 
427         * @throws Exception
428         *             _more_
429         */
430        public StormTrackCollection getTrackCollectionInner(StormInfo stormInfo,
431                        Hashtable<String, Boolean> waysToUse, Way observationWay)
432                        throws Exception {
433
434                if (observationWay == null) {
435                        observationWay = DEFAULT_OBSERVATION_WAY;
436                }
437
438                // long t1 = System.currentTimeMillis();
439                StormTrackCollection trackCollection = new StormTrackCollection();
440                // initializeStormData();
441                List<Way> forecastWays = getForecastWays(stormInfo);
442
443                for (Way forecastWay : forecastWays) {
444                        if ((waysToUse != null) && (waysToUse.size() > 0)
445                                        && (waysToUse.get(forecastWay.getId()) == null)) {
446                                continue;
447                        }
448                        List forecastTracks = getForecastTracks(stormInfo, forecastWay);
449                        if (forecastTracks.size() > 0) {
450                                trackCollection.addTrackList(forecastTracks);
451                        }
452                }
453                StormTrack obsTrack = getObservationTrack(stormInfo, observationWay);
454                // (Way) forecastWays.get(0));
455                if (obsTrack != null) {
456                        List<StormTrack> tracks = trackCollection.getTracks();
457                        // for (StormTrack stk : tracks) {
458                        // addDistanceError(obsTrack, stk);
459                        // }
460                        // long t2 = System.currentTimeMillis();
461                        // System.err.println("time:" + (t2 - t1));
462                        trackCollection.addTrack(obsTrack);
463                }
464                return trackCollection;
465        }
466
467        /**
468         * _more_
469         * 
470         * 
471         * 
472         * @param stormInfo
473         *            _more_
474         * @param forecastWay
475         *            _more_
476         * 
477         * @return _more_
478         * @throws Exception
479         *             _more_
480         */
481        private List<StormTrack> getForecastTracks(StormInfo stormInfo,
482                        Way forecastWay) throws Exception {
483
484                List<StormTrack> tracks = new ArrayList<StormTrack>();
485                List<DateTime> startDates = getForecastTrackStartDates(stormInfo,
486                                forecastWay);
487
488                int nstarts = startDates.size();
489                for (int i = 0; i < nstarts; i++) {
490                        DateTime dt = (DateTime) startDates.get(i);
491                        StormTrack tk = getForecastTrack(stormInfo, dt, forecastWay);
492                        if (tk != null) {
493                                int pn = tk.getTrackPoints().size();
494                                // Why > 1???
495                                if (pn > 1) {
496                                        tracks.add(tk);
497                                }
498                        }
499                }
500                return tracks;
501
502        }
503
504        /**
505         * If d is a missing value return NaN. Else return d
506         * 
507         * @param d
508         *            is checked if not missing return same value
509         * @param name
510         *            _more_
511         * 
512         * @return _more_
513         */
514
515        public double getValue(double d, String name) {
516                if ((d == 9999) || (d == 999)) {
517                        return Double.NaN;
518                }
519
520                if (name.equalsIgnoreCase(PARAM_MAXWINDSPEED.getName())) {
521                        if ((d < 0) || (d > 60)) {
522                                return Double.NaN;
523                        }
524                } else if (name.equalsIgnoreCase(PARAM_MINPRESSURE.getName())) {
525                        if ((d < 800) || (d > 1050)) {
526                                return Double.NaN;
527                        }
528                } else if (name.equalsIgnoreCase(PARAM_RADIUSMODERATEGALE.getName())) {
529                        if ((d < 0) || (d > 900)) {
530                                return Double.NaN;
531                        }
532                } else if (name.equalsIgnoreCase(PARAM_RADIUSWHOLEGALE.getName())) {
533                        if ((d < 0) || (d > 500)) {
534                                return Double.NaN;
535                        }
536                } else if (name.equalsIgnoreCase(PARAM_MOVESPEED.getName())) {
537                        if ((d < 0) || (d > 55)) {
538                                return Double.NaN;
539                        }
540                } else if (name.equalsIgnoreCase(PARAM_MOVEDIRECTION.getName())) {
541                        if ((d < 0) || (d > 360)) {
542                                return Double.NaN;
543                        }
544                }
545
546                return d;
547        }
548
549        /**
550         * _more_
551         * 
552         * @param d
553         *            _more_
554         * 
555         * @return _more_
556         */
557        public double getLatLonValue(double d) {
558                if ((d == 9999) || (d == 999)) {
559                        return Double.NaN;
560                }
561                return d;
562        }
563
564        /**
565         * _more_
566         * 
567         * 
568         * 
569         * @param stormInfo
570         *            _more_
571         * @param sTime
572         *            _more_
573         * @param forecastWay
574         *            _more_
575         * 
576         * @return _more_
577         * @throws Exception
578         *             _more_
579         */
580        private StormTrack getForecastTrack(StormInfo stormInfo, DateTime sTime,
581                        Way forecastWay) throws Exception {
582
583                StormTrack track = null;
584
585                Iterator iter = stormTracks.iterator();
586                String sid = stormInfo.getStormId();
587                String sway = forecastWay.getId();
588                while (iter.hasNext()) {
589                        track = (StormTrack) iter.next();
590                        String away = track.getWay().getId();
591                        String id = track.getStormInfo().getStormId();
592                        DateTime dt = track.getStartTime();
593                        if (id.equalsIgnoreCase(sid) && (dt == sTime)
594                                        && sway.equalsIgnoreCase(away)) {
595                                return track;
596                        }
597
598                }
599                return null;
600        }
601
602        /**
603         * _more_
604         * 
605         * @param year
606         *            _more_
607         * @param month
608         *            _more_
609         * @param day
610         *            _more_
611         * @param hour
612         *            _more_
613         * 
614         * @return _more_
615         * 
616         * @throws Exception
617         *             _more_
618         */
619        private DateTime getDateTime(int year, int month, int day, int hour)
620                        throws Exception {
621                GregorianCalendar convertCal = new GregorianCalendar(
622                                DateUtil.TIMEZONE_GMT);
623                convertCal.clear();
624                convertCal.set(Calendar.YEAR, year);
625                // The MONTH is 0 based. The incoming month is 1 based
626                convertCal.set(Calendar.MONTH, month - 1);
627                convertCal.set(Calendar.DAY_OF_MONTH, day);
628                convertCal.set(Calendar.HOUR_OF_DAY, hour);
629                return new DateTime(convertCal.getTime());
630        }
631
632        /**
633         * _more_
634         * 
635         * 
636         * 
637         * @param stormInfo
638         *            _more_
639         * @param way
640         *            _more_
641         * 
642         * @return _more_
643         * @throws Exception
644         *             _more_
645         */
646        protected List<DateTime> getForecastTrackStartDates(StormInfo stormInfo,
647                        Way way) throws Exception {
648
649                Iterator iter = stormTracks.iterator();
650                List<DateTime> startDates = new ArrayList<DateTime>();
651                while (iter.hasNext()) {
652                        StormTrack track = (StormTrack) iter.next();
653                        if (!track.getWay().isObservation()) {
654                                DateTime dt = track.getStartTime();
655                                startDates.add(dt);
656                        }
657                }
658                return startDates;
659        }
660
661        /**
662         * _more_
663         * 
664         * @param stormInfo
665         *            _more_
666         * @param observationWay
667         *            _more_
668         * 
669         * @return _more_
670         * 
671         * @throws Exception
672         *             _more_
673         */
674        protected StormTrack getObservationTrack(StormInfo stormInfo,
675                        Way observationWay) throws Exception {
676                addWay(observationWay);
677                // first get the obs from one specific way
678                List<StormTrackPoint> obsTrackPoints = getObservationTrackPoints(
679                                stormInfo, observationWay);
680
681                if ((obsTrackPoints == null) || (obsTrackPoints.size() == 0)) {
682                        return null;
683                }
684
685                return new StormTrack(stormInfo, addWay(Way.OBSERVATION),
686                                obsTrackPoints, obsParams);
687        }
688
689        /**
690         * _more_
691         * 
692         * @return _more_
693         */
694        public boolean getIsObservationWayChangeable() {
695                return true;
696        }
697
698        /**
699         * _more_
700         * 
701         * 
702         * 
703         * @param stormInfo
704         *            _more_
705         * @param wy
706         *            _more_
707         * 
708         * @return _more_
709         * @throws Exception
710         *             _more_
711         */
712        protected List<StormTrackPoint> getObservationTrackPoints(
713                        StormInfo stormInfo, Way wy) throws Exception {
714
715                Iterator iter = stormTracks.iterator();
716                String sway = wy.getId();
717                String sid = stormInfo.getStormId();
718
719                while (iter.hasNext()) {
720                        StormTrack strack = (StormTrack) iter.next();
721                        String away = strack.getWay().getId();
722                        String aid = strack.getStormInfo().getStormId();
723                        if (away.equalsIgnoreCase(sway) && aid.equalsIgnoreCase(sid)) {
724                                return strack.getTrackPoints();
725                        }
726
727                }
728
729                return null;
730        }
731
732        /**
733         * _more_
734         * 
735         * @param stormInfo
736         *            _more_
737         * @param wy
738         *            _more_
739         * @param before
740         *            _more_
741         * @param after
742         *            _more_
743         * @param pts
744         *            _more_
745         * 
746         * @return _more_
747         * 
748         * @throws Exception
749         *             _more_
750         */
751        protected List<StormTrackPoint> getObservationTrack(StormInfo stormInfo,
752                        Way wy, DateTime before, DateTime after, List pts) throws Exception {
753
754                return null;
755        }
756
757        /**
758         * _more_
759         * 
760         * @param times
761         *            _more_
762         * 
763         * @return _more_
764         */
765        protected DateTime getStartTime(List times) {
766                int size = times.size();
767                DateTime dt = (DateTime) times.get(0);
768                int idx = 0;
769                double value = dt.getValue();
770                for (int i = 1; i < size; i++) {
771                        dt = (DateTime) times.get(i);
772                        double dtValue = dt.getValue();
773                        if (dtValue < value) {
774                                value = dtValue;
775                                idx = i;
776                        }
777                }
778                return (DateTime) times.get(idx);
779        }
780
781        /**
782         * _more_
783         * 
784         * @param sid
785         *            _more_
786         * 
787         * @return _more_
788         * 
789         * @throws Exception
790         *             _more_
791         */
792        protected DateTime getStormStartTime(String sid) throws Exception {
793
794                Iterator iter = stormTracks.iterator();
795
796                while (iter.hasNext()) {
797                        StormTrack strack = (StormTrack) iter.next();
798                        String aid = strack.getStormInfo().getStormId();
799                        if (aid.equalsIgnoreCase(sid)) {
800                                return strack.getStartTime();
801                        }
802                }
803                return null;
804        }
805
806        /**
807         * _more_
808         * 
809         * 
810         * 
811         * @param stormInfo
812         *            _more_
813         * 
814         * @return _more_
815         * @throws Exception
816         *             _more_
817         */
818        protected List<Way> getForecastWays(StormInfo stormInfo) throws Exception {
819
820                List<Way> ways = new ArrayList();
821
822                Collection wc = stormWays.values();
823                Iterator iter = wc.iterator();
824
825                while (iter.hasNext()) {
826                        Way way = (Way) iter.next();
827                        if (!way.isObservation())
828                                ways.add(way);
829                }
830
831                // System.err.println ("ways:" + forecastWays);
832                return ways;
833
834        }
835
836        /**
837         * _more_
838         * 
839         * @param args
840         *            _more_
841         * 
842         * @throws Exception
843         *             _more_
844         */
845        public static void main(String[] args) throws Exception {
846                String sid = "0623";
847                STIStormDataSource s = null;
848                try {
849                        s = new STIStormDataSource();
850                } catch (Exception exc) {
851                        logger.error("Problem creating new STIStormDataSource", exc);
852                }
853                s.initAfter();
854                List sInfoList = s.getStormInfos();
855                StormInfo sInfo = (StormInfo) sInfoList.get(0);
856                sInfo = s.getStormInfo(sid);
857                String sd = sInfo.getStormId();
858                StormTrackCollection cls = s.getTrackCollection(sInfo, null, null);
859                StormTrack obsTrack = cls.getObsTrack();
860                List trackPointList = obsTrack.getTrackPoints();
861                List trackPointTime = obsTrack.getTrackTimes();
862                List ways = cls.getWayList();
863                Map mp = cls.getWayToStartDatesHashMap();
864                Map mp1 = cls.getWayToTracksHashMap();
865
866                System.err.println("test:");
867
868        }
869
870}