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.adt; 030 031public class Output { 032 033 static String[] Months = { "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", 034 "OCT", "NOV", "DEC" }; 035 036 /** N/S hemisphere character value */ 037 static String[] LatNS = { "N", "S" }; 038 039 /** E/W hemisphere character value */ 040 static String[] LonWE = { "W", "E" }; 041 042 /** Rule 9 string array */ 043 static String[] Rule9String = { "OFF ", "ON ", "WEAKEN" }; 044 045 /** rapid dissipation string array */ 046 static String[] RapidString = { "OFF ", "FLAG ", "ON ", "ON " }; 047 048 /** BD curve value string array */ 049 static String[] BDCatString = { "LOW CLD", "OFF WHT", "DK GRAY", "MD GRAY", "LT GRAY", 050 "BLACK ", "WHITE " }; 051 052 /** ocean basin string array */ 053 static String[] BasinString = { "ATLANTIC ", "WEST PACIFIC", "EAST PACIFIC", "INDIAN " }; 054 055 /** eye scene type array */ 056 static String[] EyeSceneTypes = { "EYE", "PINHOLE EYE", "LARGE EYE", "NONE" }; 057 058 /** cloud scene type array */ 059 static String[] CloudSceneTypes = { "UNIFORM CDO", "EMBEDDED CENTER", "IRREGULAR CDO", 060 "CURVED BAND", "SHEAR", "EYE" }; 061 062 /** storm position type arrays */ 063 static String[] AutoPosString = { "MANUAL", "FORECAST INTERPOLATION", "LAPLACIAN ANALYSIS", 064 "WARMEST PIXEL SEARCH", "SPIRAL ANALYSIS", "RING/SPIRAL COMBINATION", 065 "LINEAR EXTRAPOLATION", "NETCDF NOMINAL POSITION", "NOT AVAILABLE" }; 066 067 static String[] AutoPosStringAbbr = { " MAN ", "FCST ", "LAPL ", "WARM ", "SPRL ", "COMBO", 068 "EXTRP", "NETCDF", " N/A " }; 069 070 /** basin ID string array */ 071 static String[] PW_BasinValues = { "ATLANTIC", "PACIFIC " }; 072 073 /** Rule 8 string array */ 074 static String[] Rule8String = { "NO LIMIT ", "0.5T/6hr ", "1.0T/6hr ", "1.7T/12hr", 075 "2.2T/18hr", "2.7T/24hr", " ", " ", "0.2T/hour", "0.5T/hour", 076 "NO LIMIT ", "0.5T/6hr ", "1.0T/6hr ", "2.7T/12hr", "3.2T/18hr", "3.7T/24hr", 077 " ", " ", "0.2T/hour", "0.5T/hour", "NO LIMIT ", "0.5T/6hr ", 078 "0.7T/6hr ", "1.2T/12hr", "1.7T/18hr", "2.2T/24hr", " ", " ", 079 "0.2T/hour", "0.5T/hour", "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], 115 YearValue); 116 117 /* format character string for time output */ 118 String TimeString = String.format(" %06d UTC", CurTime); 119 120 /* convert xx.xxxx latitude format to degree/minute/second format */ 121 int[] ReturnValues2A = adt_lldms(CurLatitudeValue); 122 int DegreeValue2A = ReturnValues2A[0]; 123 int MinuteValue2A = ReturnValues2A[1]; 124 int SecondValue2A = ReturnValues2A[2]; 125 int LatNSval = (CurLatitudeValue < 0.0) ? 1 : 0; 126 /* 127 * LatNSval=0; if(CurLatitudeValue<0.0) { LatNSval=1; } 128 */ 129 /* format character string for latitude output */ 130 String LatitudeString = String.format("%3d:%02d:%02d %1s", DegreeValue2A, MinuteValue2A, 131 SecondValue2A, LatNS[LatNSval]); 132 133 int[] ReturnValues2B = adt_lldms(CurLongitudeValue); 134 int DegreeValue2B = ReturnValues2B[0]; 135 int MinuteValue2B = ReturnValues2B[1]; 136 int SecondValue2B = ReturnValues2B[2]; 137 /* 138 * int LonWEval = (CurLongitudeValue<0.0) ? 1 : 0; old McIDAS-X 139 * conversion 140 */ 141 int LonWEval = (CurLongitudeValue < 0.0) ? 0 : 1; 142 /* 143 * LonWEval=0; if(CurLongitudeValue<0.0) { LonWEval=1; } 144 */ 145 /* format character string for latitude output */ 146 String LongitudeString = String.format("%3d:%02d:%02d %1s", DegreeValue2B, MinuteValue2B, 147 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, 161 CurLongitudeValue); 162 PresWindValue_Wind = Functions.adt_getpwval(1, CurCI, CurLatitudeValue, CurLongitudeValue); 163 164 boolean Vmax1or10TF = Env.Vmax1or10TF; 165 166 if (!Vmax1or10TF) { 167 /* convert 1-minute to 10-minute average Vmax for output */ 168 PresWindValue_Wind = 0.88 * PresWindValue_Wind; 169 } 170 171 /* determine Rule 8 and Rule 9 screen output values */ 172 int Rule8Value = History.IRCurrentRecord.rule8; 173 int Rule9Value = History.IRCurrentRecord.rule9; 174 int RapidIntenValue = History.IRCurrentRecord.rapiddiss; 175 176 double EyeTempValue = History.IRCurrentRecord.eyet; 177 double CloudTempValue = History.IRCurrentRecord.cloudt; 178 179 /* determine scenetype to be output to screen */ 180 int EyeSceneTypeValue = History.IRCurrentRecord.eyescene; 181 int CloudSceneTypeValue = History.IRCurrentRecord.cloudscene; 182 183 if (CloudSceneTypeValue == 2) { 184 SceneString = String.format("%s", CloudSceneTypes[CloudSceneTypeValue]); 185 } else if (CloudSceneTypeValue == 3) { 186 double CurvedBandValue = ((double) (History.IRCurrentRecord.ringcbval - 1)) / 24.0; 187 double CurvedBandMaxValue = ((double) (History.IRCurrentRecord.ringcbvalmax - 1)) / 25.0; 188 int RingCB = History.IRCurrentRecord.ringcb; 189 SceneString = String.format("CURVED BAND with %4.2f ARC in %s", CurvedBandValue, 190 BDCatString[RingCB]); 191 /* 192 * System.out.printf("curved band max=%f curved band=%f\n", 193 * CurvedBandMaxValue,CurvedBandValue); 194 */ 195 if (CurvedBandMaxValue > CurvedBandValue) { 196 MaxCurvedBandTF = true; 197 } 198 if (MaxCurvedBandTF) { 199 SceneMaxCBString = String.format("Maximum CURVED BAND with %4.2f ARC in %s", 200 CurvedBandMaxValue, BDCatString[RingCB]); 201 /* 202 * convert xx.xxxx latitude format to degree/minute/second 203 * format 204 */ 205 double CurvedBandMaxLatitude = History.IRCurrentRecord.ringcbvalmaxlat; 206 int[] ReturnValues4A = adt_lldms(CurvedBandMaxLatitude); 207 int DegreeValue4A = ReturnValues4A[0]; 208 int MinuteValue4A = ReturnValues4A[1]; 209 int SecondValue4A = ReturnValues4A[2]; 210 LatNSval = (CurLatitudeValue < 0.0) ? 1 : 0; 211 /* 212 * LatNSval=0; if(CurvedBandMaxLatitude<0.0) { LatNSval=1; } 213 */ 214 /* format character string for latitude output */ 215 String CBMaxLatString = String.format("%3d:%02d:%02d %1s", DegreeValue4A, 216 MinuteValue4A, SecondValue4A, LatNS[LatNSval]); 217 218 /* 219 * convert xx.xxxx longitude format to degree/minute/second 220 * format 221 */ 222 double CurvedBandMaxLongitude = History.IRCurrentRecord.ringcbvalmaxlon; 223 int[] ReturnValues4B = adt_lldms(CurvedBandMaxLongitude); 224 int DegreeValue4B = ReturnValues4B[0]; 225 int MinuteValue4B = ReturnValues4B[1]; 226 int SecondValue4B = ReturnValues4B[2]; 227 /* 228 * LonWEval = (CurLongitudeValue<0.0) ? 1 : 0; old conversion 229 * for McIDAS-X 230 */ 231 LonWEval = (CurLongitudeValue < 0.0) ? 0 : 1; 232 /* 233 * LonWEval=0; if(CurvedBandMaxLongitude<0.0) { LonWEval=1; } 234 */ 235 /* format character string for longitude output */ 236 String CBMaxLonString = String.format("%3d:%02d:%02d %1s", DegreeValue4B, 237 MinuteValue4B, SecondValue4B, LonWE[LonWEval]); 238 SceneMaxCBLLString = String.format(" at Lat:%12s Lon:%12s", CBMaxLatString, 239 CBMaxLonString); 240 } 241 } else if (CloudSceneTypeValue == 4) { 242 double CDOSizeValue = History.IRCurrentRecord.eyecdosize / 110.0; 243 if (CDOSizeValue < 1.30) { 244 SceneString = String.format("SHEAR (%4.2f^ TO DG)", CDOSizeValue); 245 } else { 246 SceneString = String.format("SHEAR (>1.25^ TO DG)"); 247 } 248 } else { 249 if (EyeSceneTypeValue <= 2) { 250 SceneString = String.format("%s ", EyeSceneTypes[EyeSceneTypeValue]); 251 if (EyeSceneTypeValue <= 2) { 252 RadiusMaxWindTF = true; 253 } 254 double RMWValue = History.IRCurrentRecord.rmw; 255 if (RMWValue < 0.0) { 256 if (EyeSceneTypeValue == 1) { 257 RadiusMaxWindString = String.format("<10"); 258 259 } else { 260 RadiusMaxWindString = String.format("N/A"); 261 } 262 } else { 263 RadiusMaxWindString = String.format("%3d", (int) RMWValue); 264 } 265 } else { 266 if ((Rule8Value == 31) || (Rule8Value == 32)) { 267 SceneString = String.format("%s CLOUD REGION w/ MW EYE", 268 CloudSceneTypes[CloudSceneTypeValue]); 269 } else { 270 SceneString = String.format("%s CLOUD REGION", 271 CloudSceneTypes[CloudSceneTypeValue]); 272 } 273 } 274 } 275 276 int NumRecsHistory = History.HistoryNumberOfRecords(); 277 double InitStrengthValue = Env.InitRawTValue; 278 if ((HistoryFileName != null) && (NumRecsHistory == 0)) { 279 CloudSceneTypeValue = 0; 280 if (InitStrengthValue > 1.0) { 281 SceneString = String.format("USER DEFINED INITIAL CLASSIFICATION"); 282 } else { 283 if (InitStrengthValue == 1.0) { 284 SceneString = String.format("INITIAL CLASSIFICATION"); 285 } 286 } 287 } 288 if (InitStrengthValue < 0.0) { 289 SceneString = String.format("USER REINITIALIZED CLASSIFICATION"); 290 } 291 String SceneString2 = String.format("%s", SceneString); 292 if (MaxCurvedBandTF) { 293 SceneMaxCBString2 = String.format("%s", SceneMaxCBString); 294 SceneMaxCBLLString2 = String.format("%s", SceneMaxCBLLString); 295 } 296 297 int SatType = History.IRCurrentRecord.sattype; 298 int AutoPos = History.IRCurrentRecord.autopos; 299 int LandFlag = History.IRCurrentRecord.land; 300 int R34Value = History.IRCurrentRecord.r34; 301 int MSLPenvValue = History.IRCurrentRecord.MSLPenv; 302 double VZAValue = History.IRCurrentRecord.vza; 303 String SatelliteIDString = Functions.adt_sattypes(SatType); 304 305 String VersionString = Env.ADTVersion; 306 boolean LandFlagTF = Env.LandFlagTF; 307 boolean UseCKZTF = Env.UseCKZTF; 308 309 /* send results to the screen */ 310 311 TextScreen_Return += String 312 .format("\n****************************************************\n\n"); 313 TextScreen_Return += String 314 .format(" UW - CIMSS \n"); 315 TextScreen_Return += String.format(" ADVANCED DVORAK TECHNIQUE \n"); 316 TextScreen_Return += String.format(" %17s \n", 317 VersionString); 318 TextScreen_Return += String 319 .format(" Tropical Cyclone Intensity Algorithm \n\n"); 320 TextScreen_Return += String.format(" ----- Current Analysis ----- \n"); 321 TextScreen_Return += String.format(" Date : %12s Time : %12s\n", DateString, 322 TimeString); 323 TextScreen_Return += String.format(" Lat : %12s Lon : %12s\n\n", LatitudeString, 324 LongitudeString); 325 if ((LandFlagTF) && (LandFlag == 1)) { 326 TextScreen_Return += String.format(" TROPICAL CYCLONE OVER LAND\n"); 327 TextScreen_Return += String.format(" NO ADT ANALYSIS AVAILABLE\n"); 328 } else { 329 if (Vmax1or10TF) { 330 TextScreen_Return += String.format(" CI# /Pressure/ Vmax\n"); 331 } else { 332 TextScreen_Return += String.format(" CI# /Pressure/ Vmax(10-min)\n"); 333 } 334 if (UseCKZTF) { 335 TextScreen_Return += String.format(" %3.1f /%6.1fmb/%5.1fkt\n\n", 336 CurCI, PresWindValue_Pressure, PresWindValue_Wind); 337 } else { 338 TextScreen_Return += String.format(" %3.1f /%6.1fmb/%5.1fkt\n\n", 339 CurCI, PresWindValue_Pressure + CurCIAdjP, PresWindValue_Wind); 340 } 341 if (HistoryFileName != null) { 342 TextScreen_Return += String.format(" Final T# Adj T# Raw T# \n"); 343 TextScreen_Return += String.format(" %3.1f %3.1f %3.1f\n\n", 344 CurFinalT, CurRawT, CurRawTorig); 345 } 346 if (!UseCKZTF) { 347 TextScreen_Return += String.format( 348 " Latitude bias adjustment to MSLP : %+5.1fmb\n\n", CurCIAdjP); 349 } 350 if (RadiusMaxWindTF) { 351 TextScreen_Return += String.format( 352 " Estimated radius of max. wind based on IR :%3s km\n\n", 353 RadiusMaxWindString); 354 } 355 TextScreen_Return += String.format( 356 " Center Temp : %+5.1fC Cloud Region Temp : %5.1fC\n", EyeTempValue, 357 CloudTempValue); 358 TextScreen_Return += String.format("\n Scene Type : %s \n", SceneString2); 359 if (MaxCurvedBandTF) { 360 TextScreen_Return += String.format(" %s \n", SceneMaxCBString2); 361 TextScreen_Return += String.format(" %s \n", SceneMaxCBLLString2); 362 } 363 TextScreen_Return += String.format("\n Positioning Method : %s \n", 364 AutoPosString[AutoPos]); 365 TextScreen_Return += String.format("\n Ocean Basin : %12s \n", 366 BasinString[BasinIDValue]); 367 TextScreen_Return += String.format(" Dvorak CI > MSLP Conversion Used : %8s \n", 368 PW_BasinValues[DomainID]); 369 if (HistoryFileName != null) { 370 TextScreen_Return += String.format("\n Tno/CI Rules : Constraint Limits : %9s\n", 371 Rule8String[Rule8Value]); 372 TextScreen_Return += String.format(" Weakening Flag : %6s\n", 373 Rule9String[Rule9Value]); 374 TextScreen_Return += String.format(" Rapid Dissipation Flag : %6s\n", 375 RapidString[RapidIntenValue]); 376 } 377 if (UseCKZTF) { 378 TextScreen_Return += String.format("\n C/K/Z MSLP Estimate Inputs :\n"); 379 if (R34Value > 0.0) { 380 TextScreen_Return += String.format(" - Average 34 knot radii : %4dkm\n", 381 R34Value); 382 } else { 383 TextScreen_Return += String.format(" - Average 34 knot radii : N/A\n"); 384 } 385 TextScreen_Return += String.format(" - Environmental MSLP : %4dmb\n", 386 MSLPenvValue); 387 } 388 TextScreen_Return += String.format("\n Satellite Name : %7s \n", SatelliteIDString); 389 TextScreen_Return += String.format(" Satellite Viewing Angle : %4.1f degrees \n", 390 VZAValue); 391 } 392 TextScreen_Return += String 393 .format("\n****************************************************\n\n"); 394 395 System.err.println("TextScreenOutput() out, val: " + TextScreen_Return); 396 return TextScreen_Return; 397 } 398 399 /** 400 * Convert "degree.partial_degree" to degree/minute/second format. 401 * 402 * @param FullDegreeValue 403 * Latitude/Longitude value to convert. 404 * 405 * @return Array whose values represent (in order): degrees, minutes, and 406 * seconds. 407 */ 408 public static int[] adt_lldms(double FullDegreeValue) { 409 int DegreeValue = (int) FullDegreeValue; 410 double MinuteValue = (FullDegreeValue - (double) DegreeValue) * 60.0; 411 double SecondValue = (MinuteValue - (double) ((int) MinuteValue)) * 60.0; 412 int DegreeReturn = Math.abs(DegreeValue); 413 int MinuteReturn = (int) Math.abs(MinuteValue); 414 int SecondReturn = (int) Math.abs(SecondValue); 415 416 return new int[] { DegreeReturn, MinuteReturn, SecondReturn }; 417 } 418}