001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2017
005 * Space Science and Engineering Center (SSEC)
006 * University of Wisconsin - Madison
007 * 1225 W. Dayton Street, Madison, WI 53706, USA
008 * https://www.ssec.wisc.edu/mcidas
009 * 
010 * All Rights Reserved
011 * 
012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
013 * some McIDAS-V source code is based on IDV and VisAD source code.  
014 * 
015 * McIDAS-V is free software; you can redistribute it and/or modify
016 * it under the terms of the GNU Lesser Public License as published by
017 * the Free Software Foundation; either version 3 of the License, or
018 * (at your option) any later version.
019 * 
020 * McIDAS-V is distributed in the hope that it will be useful,
021 * but WITHOUT ANY WARRANTY; without even the implied warranty of
022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
023 * GNU Lesser Public License for more details.
024 * 
025 * You should have received a copy of the GNU Lesser Public License
026 * along with this program.  If not, see http://www.gnu.org/licenses.
027 */
028
029package edu.wisc.ssec.mcidasv.adt;
030
031public class Output {
032
033   static String[] Months = { "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" };
034   
035   /** N/S hemisphere character value */
036   static String[] LatNS = {"N","S"};
037   
038   /** E/W hemisphere character value */
039   static String[] LonWE = {"W","E"};
040   
041   /** Rule 9 string array */
042   static String[] Rule9String = { "OFF   ","ON    ","WEAKEN" };
043   
044   /** rapid dissipation string array */
045   static String[] RapidString = { "OFF   ","FLAG  ","ON    ","ON    " };
046   
047   /** BD curve value string array */
048   static String[] BDCatString = { "LOW CLD","OFF WHT","DK GRAY","MD GRAY", "LT GRAY","BLACK  ","WHITE  " };
049   
050   /** ocean basin string array */
051   static String[] BasinString = { "ATLANTIC    ","WEST PACIFIC", "EAST PACIFIC","INDIAN      " };
052   
053   /** eye scene type array */
054   static String[] EyeSceneTypes = { "EYE","PINHOLE EYE","LARGE EYE","NONE" };
055   
056   /** cloud scene type array */
057   static String[] CloudSceneTypes = { "UNIFORM CDO","EMBEDDED CENTER","IRREGULAR CDO","CURVED BAND","SHEAR","EYE" };
058   
059   /** storm position type arrays */
060   static String[] AutoPosString = { "MANUAL", "FORECAST INTERPOLATION", "LAPLACIAN ANALYSIS", "WARMEST PIXEL SEARCH",
061                                     "SPIRAL ANALYSIS", "RING/SPIRAL COMBINATION", "LINEAR EXTRAPOLATION",
062                                     "NETCDF NOMINAL POSITION", "NOT AVAILABLE"};
063   
064   static String[] AutoPosStringAbbr = { " MAN ","FCST ", "LAPL ", "WARM ", "SPRL ", "COMBO", "EXTRP", "NETCDF", " N/A " };
065   
066   /** basin ID string array */
067   static String[] PW_BasinValues = { "ATLANTIC","PACIFIC " };
068   
069   /** Rule 8 string array */
070   static String[] Rule8String = { "NO LIMIT ","0.5T/6hr ","1.0T/6hr ","1.7T/12hr",
071                                   "2.2T/18hr","2.7T/24hr","         ","         ",
072                                   "0.2T/hour","0.5T/hour",
073                                  "NO LIMIT ","0.5T/6hr ","1.0T/6hr ","2.7T/12hr",
074                                   "3.2T/18hr","3.7T/24hr","         ","         ",
075                                   "0.2T/hour","0.5T/hour",
076                                  "NO LIMIT ","0.5T/6hr ","0.7T/6hr ","1.2T/12hr",
077                                   "1.7T/18hr","2.2T/24hr","         ","         ",
078                                   "0.2T/hour","0.5T/hour",
079                                   "MW Adjst ","MW ON    ","MW ON    ","MW HOLD  ",
080                                   "MW AdjEnd" };
081
082   public Output() {
083   }
084
085   public static String TextScreenOutput(String HistoryFileName){
086
087       System.err.println("TextScreenOutput() in...");
088      String TextScreen_Return = "";
089
090      /* int LatNSval = 0; */
091      /* int LonWEval = 0; */
092      String SceneString = "";
093      String RadiusMaxWindString = "";
094      String SceneMaxCBString = "";
095      String SceneMaxCBLLString = "";
096      String SceneMaxCBString2 = "";
097      String SceneMaxCBLLString2 = "";
098      double PresWindValue_Pressure = -999.0;
099      double PresWindValue_Wind = -999.0;
100      boolean MaxCurvedBandTF = false;
101      boolean RadiusMaxWindTF = false;
102
103      /* convert Julian date/time to DateValue/month/year format */
104      int CurDate = History.IRCurrentRecord.date;
105      int CurTime = History.IRCurrentRecord.time;
106      double CurLatitudeValue = History.IRCurrentRecord.latitude;
107      double CurLongitudeValue = History.IRCurrentRecord.longitude;
108      int[] ReturnValues = Functions.adt_yddmy(CurDate);
109      int DateValue = ReturnValues[0];
110      int MonthValue = ReturnValues[1];
111      int YearValue = ReturnValues[2];
112
113      /* format character string for date output */
114      String DateString = String.format("%02d %3s %04d",DateValue,Months[MonthValue-1],YearValue);
115
116      /* format character string for time output */
117      String TimeString = String.format("  %06d UTC",CurTime);
118    
119      /* convert xx.xxxx latitude format to degree/minute/second format */
120      int[] ReturnValues2A = adt_lldms(CurLatitudeValue);
121      int DegreeValue2A = ReturnValues2A[0];
122      int MinuteValue2A = ReturnValues2A[1];
123      int SecondValue2A = ReturnValues2A[2];
124      int LatNSval = (CurLatitudeValue<0.0) ? 1 : 0;
125      /*
126      LatNSval=0;
127      if(CurLatitudeValue<0.0) {
128         LatNSval=1;
129      }
130      */
131      /* format character string for latitude output */
132      String LatitudeString = String.format("%3d:%02d:%02d %1s",DegreeValue2A,MinuteValue2A,SecondValue2A,LatNS[LatNSval]);
133
134      int[] ReturnValues2B = adt_lldms(CurLongitudeValue);
135      int DegreeValue2B = ReturnValues2B[0];
136      int MinuteValue2B = ReturnValues2B[1];
137      int SecondValue2B = ReturnValues2B[2];
138      /* int LonWEval = (CurLongitudeValue<0.0) ? 1 : 0; old McIDAS-X conversion */
139      int LonWEval = (CurLongitudeValue<0.0) ? 0 : 1;
140      /*
141      LonWEval=0;
142      if(CurLongitudeValue<0.0) {
143         LonWEval=1;
144      }
145      */
146      /* format character string for latitude output */
147      String LongitudeString = String.format("%3d:%02d:%02d %1s",DegreeValue2B,MinuteValue2B,SecondValue2B,LonWE[LonWEval]);
148
149      int[] ReturnValues3 = Functions.adt_oceanbasin(CurLatitudeValue,CurLongitudeValue);
150      int BasinIDValue = ReturnValues3[0];
151      int DomainID = ReturnValues3[1];
152    
153      /* determine Dvorak pressure/wind speed in relation to final CI # */
154      double CurRawT = History.IRCurrentRecord.Traw;
155      double CurRawTorig = History.IRCurrentRecord.TrawO;
156      double CurFinalT = History.IRCurrentRecord.Tfinal;
157      double CurCI = History.IRCurrentRecord.CI;
158      double CurCIAdjP = History.IRCurrentRecord.CIadjp;
159      /* System.out.printf("ciadjp=%f\n",CurCIAdjP); */
160      PresWindValue_Pressure = Functions.adt_getpwval(0,CurCI,CurLatitudeValue,CurLongitudeValue);
161      PresWindValue_Wind = Functions.adt_getpwval(1,CurCI,CurLatitudeValue,CurLongitudeValue);
162
163      boolean Vmax1or10TF = Env.Vmax1or10TF;
164
165      if(!Vmax1or10TF) {
166         /* convert 1-minute to 10-minute average Vmax for output */
167         PresWindValue_Wind=0.88*PresWindValue_Wind;
168      }
169    
170      /* determine Rule 8 and Rule 9 screen output values */
171      int Rule8Value = History.IRCurrentRecord.rule8;
172      int Rule9Value = History.IRCurrentRecord.rule9;
173      int RapidIntenValue = History.IRCurrentRecord.rapiddiss;
174
175      double EyeTempValue = History.IRCurrentRecord.eyet;
176      double CloudTempValue = History.IRCurrentRecord.cloudt;
177
178      /* determine scenetype to be output to screen */
179      int EyeSceneTypeValue = History.IRCurrentRecord.eyescene;
180      int CloudSceneTypeValue = History.IRCurrentRecord.cloudscene;
181
182      if(CloudSceneTypeValue==2) {
183         SceneString = String.format("%s",CloudSceneTypes[CloudSceneTypeValue]);
184      } else if(CloudSceneTypeValue==3) {
185         double CurvedBandValue = ((double)(History.IRCurrentRecord.ringcbval-1))/24.0;
186         double CurvedBandMaxValue = ((double)(History.IRCurrentRecord.ringcbvalmax-1))/25.0;
187         int RingCB = History.IRCurrentRecord.ringcb;
188         SceneString = String.format("CURVED BAND with %4.2f ARC in %s",CurvedBandValue,BDCatString[RingCB]);
189         /* System.out.printf("curved band max=%f curved band=%f\n",CurvedBandMaxValue,CurvedBandValue); */
190         if(CurvedBandMaxValue>CurvedBandValue) {
191            MaxCurvedBandTF = true;
192         }
193         if(MaxCurvedBandTF) {
194            SceneMaxCBString = String.format("Maximum CURVED BAND with %4.2f ARC in %s",CurvedBandMaxValue,BDCatString[RingCB]);
195            /* convert xx.xxxx latitude format to degree/minute/second format */
196            double CurvedBandMaxLatitude = History.IRCurrentRecord.ringcbvalmaxlat;
197            int[] ReturnValues4A = adt_lldms(CurvedBandMaxLatitude);
198            int DegreeValue4A = ReturnValues4A[0];
199            int MinuteValue4A = ReturnValues4A[1];
200            int SecondValue4A = ReturnValues4A[2];
201            LatNSval = (CurLatitudeValue<0.0) ? 1 : 0;
202            /*
203            LatNSval=0;
204            if(CurvedBandMaxLatitude<0.0) {
205               LatNSval=1;
206            }
207            */
208            /* format character string for latitude output */
209            String CBMaxLatString = String.format("%3d:%02d:%02d %1s",DegreeValue4A,MinuteValue4A,SecondValue4A,LatNS[LatNSval]);
210
211            /* convert xx.xxxx longitude format to degree/minute/second format */
212            double CurvedBandMaxLongitude = History.IRCurrentRecord.ringcbvalmaxlon;
213            int[] ReturnValues4B = adt_lldms(CurvedBandMaxLongitude);
214            int DegreeValue4B = ReturnValues4B[0];
215            int MinuteValue4B = ReturnValues4B[1];
216            int SecondValue4B = ReturnValues4B[2];
217            /* LonWEval = (CurLongitudeValue<0.0) ? 1 : 0;  old conversion for McIDAS-X */
218            LonWEval = (CurLongitudeValue<0.0) ? 0 : 1;
219            /*
220            LonWEval=0;
221            if(CurvedBandMaxLongitude<0.0) {
222               LonWEval=1;
223            }
224            */
225            /* format character string for longitude output */
226            String CBMaxLonString = String.format("%3d:%02d:%02d %1s",DegreeValue4B,MinuteValue4B,SecondValue4B,LonWE[LonWEval]);
227            SceneMaxCBLLString = String.format(" at Lat:%12s  Lon:%12s",CBMaxLatString,CBMaxLonString);
228         }
229      }
230      else if(CloudSceneTypeValue==4) {
231         double CDOSizeValue = History.IRCurrentRecord.eyecdosize/110.0;
232         if(CDOSizeValue<1.30) {
233            SceneString = String.format("SHEAR (%4.2f^ TO DG)",CDOSizeValue);
234         } else {
235            SceneString = String.format("SHEAR (>1.25^ TO DG)");
236         }
237      }
238      else {
239         if(EyeSceneTypeValue<=2) {
240            SceneString = String.format("%s ",EyeSceneTypes[EyeSceneTypeValue]);
241            if(EyeSceneTypeValue<=2) {
242               RadiusMaxWindTF = true;
243            }
244            double RMWValue = History.IRCurrentRecord.rmw;
245            if(RMWValue<0.0) {
246               if(EyeSceneTypeValue==1) {
247                  RadiusMaxWindString = String.format("<10");
248                   
249               } else {
250                  RadiusMaxWindString = String.format("N/A");
251               }
252            } else {
253               RadiusMaxWindString = String.format("%3d",(int)RMWValue);
254            }
255         }
256         else {
257            if((Rule8Value==31)||(Rule8Value==32)) {
258               SceneString = String.format("%s CLOUD REGION w/ MW EYE",CloudSceneTypes[CloudSceneTypeValue]);
259            } else {
260               SceneString = String.format("%s CLOUD REGION",CloudSceneTypes[CloudSceneTypeValue]);
261            }
262         }
263      }
264      
265      int NumRecsHistory = History.HistoryNumberOfRecords();
266      double InitStrengthValue = Env.InitRawTValue;
267      if((HistoryFileName!=null)&&(NumRecsHistory==0)) {
268         CloudSceneTypeValue=0;
269         if(InitStrengthValue>1.0) {
270            SceneString = String.format("USER DEFINED INITIAL CLASSIFICATION");
271         } else {
272            if(InitStrengthValue==1.0) {
273               SceneString = String.format("INITIAL CLASSIFICATION");
274            }
275         }
276      }
277      if(InitStrengthValue<0.0) {
278         SceneString = String.format("USER REINITIALIZED CLASSIFICATION");
279      }
280      String SceneString2 = String.format("%s",SceneString);
281      if(MaxCurvedBandTF) {
282         SceneMaxCBString2 = String.format("%s",SceneMaxCBString);
283         SceneMaxCBLLString2 = String.format("%s",SceneMaxCBLLString);
284      }
285
286      int SatType = History.IRCurrentRecord.sattype;
287      int AutoPos = History.IRCurrentRecord.autopos;
288      int LandFlag = History.IRCurrentRecord.land;
289      int R34Value = History.IRCurrentRecord.r34;
290      int MSLPenvValue = History.IRCurrentRecord.MSLPenv;
291      double VZAValue = History.IRCurrentRecord.vza;
292      String SatelliteIDString = Functions.adt_sattypes(SatType);
293      
294      String VersionString = Env.ADTVersion;
295      boolean LandFlagTF = Env.LandFlagTF;
296      boolean UseCKZTF = Env.UseCKZTF;
297
298      /* send results to the screen */
299
300      TextScreen_Return += String.format("\n****************************************************\n\n");
301      TextScreen_Return += String.format("                     UW - CIMSS                     \n");
302      TextScreen_Return += String.format("              ADVANCED DVORAK TECHNIQUE       \n");
303      TextScreen_Return += String.format("                  %17s                \n",VersionString);
304      TextScreen_Return += String.format("         Tropical Cyclone Intensity Algorithm       \n\n");
305      TextScreen_Return += String.format("             ----- Current Analysis ----- \n");
306      TextScreen_Return += String.format("     Date : %12s    Time : %12s\n",DateString,TimeString);
307      TextScreen_Return += String.format("      Lat : %12s     Lon : %12s\n\n",LatitudeString,
308                                                           LongitudeString);
309      if((LandFlagTF)&&(LandFlag==1)) {
310         TextScreen_Return += String.format("              TROPICAL CYCLONE OVER LAND\n");
311         TextScreen_Return += String.format("              NO ADT ANALYSIS AVAILABLE\n");
312      }
313      else {
314         if(Vmax1or10TF) {
315            TextScreen_Return += String.format("                CI# /Pressure/ Vmax\n");
316         } else {
317            TextScreen_Return += String.format("                CI# /Pressure/ Vmax(10-min)\n");
318         }
319         if(UseCKZTF) {
320            TextScreen_Return += String.format("                %3.1f /%6.1fmb/%5.1fkt\n\n",CurCI,
321                                               PresWindValue_Pressure,PresWindValue_Wind);
322         } else {
323            TextScreen_Return += String.format("                %3.1f /%6.1fmb/%5.1fkt\n\n",CurCI,
324                                               PresWindValue_Pressure+CurCIAdjP, PresWindValue_Wind);
325         }
326         if(HistoryFileName!=null) {
327            TextScreen_Return += String.format("             Final T#  Adj T#  Raw T# \n");
328            TextScreen_Return += String.format("                %3.1f     %3.1f     %3.1f\n\n",CurFinalT,CurRawT,CurRawTorig);
329         }
330         if(!UseCKZTF) {
331            TextScreen_Return += String.format("     Latitude bias adjustment to MSLP : %+5.1fmb\n\n",CurCIAdjP);
332         }
333         if(RadiusMaxWindTF) {
334            TextScreen_Return += String.format(" Estimated radius of max. wind based on IR :%3s km\n\n",RadiusMaxWindString);
335         }
336         TextScreen_Return += String.format(" Center Temp : %+5.1fC    Cloud Region Temp : %5.1fC\n",EyeTempValue,CloudTempValue);
337         TextScreen_Return += String.format("\n Scene Type : %s \n",SceneString2);
338         if(MaxCurvedBandTF) {
339            TextScreen_Return += String.format("              %s \n",SceneMaxCBString2);
340            TextScreen_Return += String.format("              %s \n",SceneMaxCBLLString2);
341         }
342         TextScreen_Return += String.format("\n Positioning Method : %s \n",AutoPosString[AutoPos]);
343         TextScreen_Return += String.format("\n Ocean Basin : %12s  \n",BasinString[BasinIDValue]);
344         TextScreen_Return += String.format(" Dvorak CI > MSLP Conversion Used : %8s  \n",PW_BasinValues[DomainID]);
345         if(HistoryFileName!=null) {
346            TextScreen_Return += String.format("\n Tno/CI Rules : Constraint Limits : %9s\n", Rule8String[Rule8Value]);
347            TextScreen_Return += String.format("                   Weakening Flag : %6s\n", Rule9String[Rule9Value]);
348            TextScreen_Return += String.format("           Rapid Dissipation Flag : %6s\n", RapidString[RapidIntenValue]);
349         }
350         if(UseCKZTF) {
351            TextScreen_Return += String.format("\n C/K/Z MSLP Estimate Inputs :\n");
352            if(R34Value>0.0) {
353               TextScreen_Return += String.format("  - Average 34 knot radii : %4dkm\n",R34Value);
354            } else {
355               TextScreen_Return += String.format("  - Average 34 knot radii : N/A\n");
356            }
357            TextScreen_Return += String.format("  - Environmental MSLP    : %4dmb\n",MSLPenvValue);
358         }
359         TextScreen_Return += String.format("\n Satellite Name : %7s \n",SatelliteIDString);
360         TextScreen_Return += String.format(" Satellite Viewing Angle : %4.1f degrees \n",VZAValue);
361      }
362      TextScreen_Return += String.format("\n****************************************************\n\n");
363
364      System.err.println("TextScreenOutput() out, val: " + TextScreen_Return);
365      return TextScreen_Return;
366   }
367   
368   /**
369    * Convert "degree.partial_degree" to degree/minute/second format.
370    *
371    * @param FullDegreeValue Latitude/Longitude value to convert.
372    *
373    * @return Array whose values represent (in order): degrees, minutes, and
374    * seconds.
375    */
376   public static int[] adt_lldms(double FullDegreeValue) {
377      int DegreeValue = (int)FullDegreeValue;
378      double MinuteValue = (FullDegreeValue-(double)DegreeValue)*60.0;
379      double SecondValue = (MinuteValue-(double)((int)MinuteValue))*60.0;
380      int DegreeReturn = Math.abs(DegreeValue);
381      int MinuteReturn = (int)Math.abs(MinuteValue);
382      int SecondReturn = (int)Math.abs(SecondValue);
383
384      return new int[] { DegreeReturn, MinuteReturn, SecondReturn };
385   }
386}