001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2023
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 Scene {
032
033    private static double[] BDCurve_Points = { 30.0, 9.0, -30.0, -42.0, -54.0, -64.0, -70.0, -76.0,
034            -80.0, -84.0, -100.0 };
035
036    private static double LARGE_EYE_RADIUS = 38.0;
037    private static double RING_WIDTH = 4.0;
038    private static double MANUAL_EYE_RADIUS = 24.0;
039    private static float[][] IRImageLatitudeArrayLocal = new float[200][200];
040    private static float[][] IRImageLongitudeArrayLocal = new float[200][200];
041    private static float[][] IRImageTemperatureArrayLocal = new float[200][200];
042    private static int IRImageXSize;
043    private static int IRImageYSize;
044
045    public Scene() {
046        IRImageXSize = -1;
047        IRImageYSize = -1;
048    }
049
050    /**
051     * adt_classify in original ADT codebase.
052     *
053     * @param RunFullAnalysis
054     *            Whether or not a complete scene analysis should happen.
055     */
056    public static void DetermineSceneType(boolean RunFullAnalysis) {
057
058        int XInc;
059        int PreviousHistoryEyeSceneID = -1;
060        int PreviousHistoryCloudSceneID = -1;
061        double PreviousHistoryTnoValueMinus12hrs = 0.0;
062        boolean FoundHistoryRecMinus12hrTF = false;
063        double TemperatureValue = -999.0;
064        int LogSpiralAmount = 0;
065
066        int CloudBDCategory = -99;
067        int CloudCWBDCategory = -99;
068        int EyeBDCategory = -99;
069        int CloudBDDifference = -99;
070        int EyeCloudBDCategoryDifference = -99;
071        double TnoInterpValue = -99.9;
072        double CloudBDCategoryFloat = -99.9;
073        double EyeBDCategoryFloat = -99.9;
074        double CloudCWBDCategoryFloat = -99.9;
075        double CloudTemperatureDifference = -99.9;
076        double EyeCloudCWBDCategoryFloatDiff = -99.9;
077        double EyeCloudBDCategoryFloatDiff = -99.9;
078        double CloudBDCategoryFloatDiff = -99.9;
079        double EyeCloudTemperatureDiff2 = -99.9;
080
081        boolean LandFlagTF = Env.LandFlagTF;
082        double InitStrengthValue = Env.InitRawTValue;
083        double RMWSize = Env.RMWSize;
084
085        int EyeFFTValue = History.IRCurrentRecord.eyefft;
086        int CloudFFTValue = History.IRCurrentRecord.cloudfft;
087        double StormLatitude = History.IRCurrentRecord.latitude;
088        double StormLongitude = History.IRCurrentRecord.longitude;
089        double EyeTemperature = History.IRCurrentRecord.eyet;
090        double EyeStdvValue = History.IRCurrentRecord.eyestdv;
091        double CloudCWTemperature = History.IRCurrentRecord.cwcloudt;
092        double CloudTemperature = History.IRCurrentRecord.cloudt;
093        double CloudSymmetryValue = History.IRCurrentRecord.cloudsymave;
094        double CurvedBandBDMaxLatitude = StormLatitude;
095        double CurvedBandBDMaxLongitude = StormLongitude;
096
097        for (XInc = 0; XInc < 10; XInc++) {
098            /* compute cloud category */
099            if ((CloudTemperature <= BDCurve_Points[XInc])
100                    && (CloudTemperature > BDCurve_Points[XInc + 1])) {
101                CloudBDCategory = XInc;
102                TnoInterpValue = (CloudTemperature - BDCurve_Points[CloudBDCategory])
103                        / (BDCurve_Points[CloudBDCategory + 1] - BDCurve_Points[CloudBDCategory]);
104                if (CloudBDCategory == 0) {
105                    TnoInterpValue = 0.0;
106                }
107                CloudBDCategoryFloat = (double) CloudBDCategory + TnoInterpValue;
108            }
109            /* compute eye category */
110            if ((EyeTemperature <= BDCurve_Points[XInc])
111                    && (EyeTemperature > BDCurve_Points[XInc + 1])) {
112                EyeBDCategory = XInc;
113                TnoInterpValue = (EyeTemperature - BDCurve_Points[EyeBDCategory])
114                        / (BDCurve_Points[EyeBDCategory + 1] - BDCurve_Points[EyeBDCategory]);
115                if (EyeBDCategory == 0) {
116                    TnoInterpValue = 0.0;
117                }
118                EyeBDCategoryFloat = (double) EyeBDCategory + TnoInterpValue;
119            }
120            /* compute C-W eye category */
121            if ((CloudCWTemperature <= BDCurve_Points[XInc])
122                    && (CloudCWTemperature > BDCurve_Points[XInc + 1])) {
123                CloudCWBDCategory = XInc;
124                TnoInterpValue = (CloudCWTemperature - BDCurve_Points[CloudCWBDCategory])
125                        / (BDCurve_Points[CloudCWBDCategory + 1] - BDCurve_Points[CloudCWBDCategory]);
126                if (CloudCWBDCategory == 0) {
127                    TnoInterpValue = 0.0;
128                }
129                CloudCWBDCategoryFloat = (double) CloudCWBDCategory + TnoInterpValue;
130            }
131        }
132
133        /*
134         * System.out.printf("EYE = temp=%f cat=%d part=%f \n",EyeTemperature,
135         * EyeBDCategory, EyeBDCategoryFloat);
136         * System.out.printf("CLD = temp=%f cat=%d part=%f \n"
137         * ,CloudTemperature,CloudBDCategory, CloudBDCategoryFloat);
138         * System.out.printf
139         * ("CWT = temp=%f cat=%d part=%f \n",CloudCWTemperature,
140         * CloudCWBDCategory, CloudCWBDCategoryFloat);
141         */
142
143        CloudTemperatureDifference = CloudTemperature - CloudCWTemperature;
144        EyeCloudCWBDCategoryFloatDiff = CloudCWBDCategoryFloat - EyeBDCategoryFloat;
145        EyeCloudBDCategoryFloatDiff = CloudBDCategoryFloat - EyeBDCategoryFloat;
146        CloudBDCategoryFloatDiff = CloudBDCategoryFloat - CloudCWBDCategoryFloat;
147        CloudBDDifference = CloudBDCategory - CloudCWBDCategory;
148        EyeCloudBDCategoryDifference = CloudBDCategory - EyeBDCategory;
149        EyeCloudTemperatureDiff2 = EyeTemperature
150                - (Math.min(CloudTemperature, CloudCWTemperature));
151
152        /*
153         * System.out.printf("BDCategoryDifference=%d\n",BDCategoryDifference);
154         * System.out.printf("EyeCloudBDCategoryDifference=%d\n",
155         * EyeCloudBDCategoryDifference);
156         * System.out.printf("CloudBDDifference=%d\n",CloudBDDifference);
157         * System.
158         * out.printf("CloudTemperatureDifference=%f\n",CloudTemperatureDifference
159         * ); System.out.printf("EyeCloudTemperatureDifference=%f\n",
160         * EyeCloudTemperatureDifference);
161         * System.out.printf("EyeCloudCWBDCategoryFloatDiff=%f\n"
162         * ,EyeCloudCWBDCategoryFloatDiff);
163         * System.out.printf("EyeCloudBDCategoryFloatDiff=%f\n"
164         * ,EyeCloudBDCategoryFloatDiff);
165         * System.out.printf("CloudBDCategoryFloatDiff=%f\n"
166         * ,CloudBDCategoryFloatDiff);
167         * System.out.printf("EyeCloudTemperatureDiff2=%f\n"
168         * ,EyeCloudTemperatureDiff2);
169         */
170
171        int ImageDate = History.IRCurrentRecord.date;
172        int ImageTime = History.IRCurrentRecord.time;
173
174        double CurrentTime = Functions.calctime(ImageDate, ImageTime);
175        /* System.out.printf("current time=%f\n",CurrentTime); */
176
177        double CurrentTimeMinus12hr = CurrentTime - 0.5;
178        boolean FoundEyeSceneTF = false;
179        double MaximumRule9Value = -99.0;
180        int LastRule9Value = 0;
181        double PreviousHistoryTnoValue = MaximumRule9Value;
182        double PreviousValidHistoryTnoValue = PreviousHistoryTnoValue;
183
184        int HistoryFileRecords = History.HistoryNumberOfRecords();
185
186        if ((HistoryFileRecords == 0) || (!RunFullAnalysis)) {
187            FoundHistoryRecMinus12hrTF = true;
188            PreviousHistoryEyeSceneID = 3;
189            LastRule9Value = 1;
190            if ((CloudCWBDCategoryFloat < 3.5) && (InitStrengthValue < 3.5)) {
191                PreviousHistoryCloudSceneID = 3;
192                PreviousHistoryTnoValueMinus12hrs = InitStrengthValue;
193            } else {
194                PreviousHistoryCloudSceneID = 0;
195                PreviousHistoryTnoValueMinus12hrs = Math.max(InitStrengthValue, 4.0);
196            }
197
198        } else {
199            FoundHistoryRecMinus12hrTF = false;
200            PreviousHistoryCloudSceneID = 3;
201
202            int RecDate, RecTime, RecLand;
203            int RecEyeScene, RecCloudScene;
204            int RecRule9;
205            double LastValidHistoryRecTime = 0.0;
206            double HistoryRecTime;
207            double RecTnoRaw, RecTnoFinal;
208            boolean LandCheckTF;
209            for (XInc = 0; XInc < HistoryFileRecords; XInc++) {
210                RecDate = History.HistoryFile[XInc].date;
211                RecTime = History.HistoryFile[XInc].time;
212                RecLand = History.HistoryFile[XInc].land;
213                RecTnoRaw = History.HistoryFile[XInc].Traw;
214                HistoryRecTime = Functions.calctime(RecDate, RecTime);
215                LandCheckTF = true;
216                if (((LandFlagTF) && (RecLand == 1)) || (RecTnoRaw < 1.0)) {
217                    LandCheckTF = false;
218                }
219                if ((HistoryRecTime < CurrentTime) && (LandCheckTF)) {
220                    LastValidHistoryRecTime = HistoryRecTime;
221                    RecTnoFinal = History.HistoryFile[XInc].Tfinal;
222                    RecEyeScene = History.HistoryFile[XInc].eyescene;
223                    RecCloudScene = History.HistoryFile[XInc].cloudscene;
224                    RecRule9 = History.HistoryFile[XInc].rule9;
225                    if ((HistoryRecTime >= CurrentTimeMinus12hr) && (!FoundHistoryRecMinus12hrTF)) {
226                        PreviousHistoryTnoValueMinus12hrs = RecTnoFinal;
227                        FoundHistoryRecMinus12hrTF = true;
228                    }
229                    PreviousHistoryTnoValue = RecTnoFinal;
230                    PreviousHistoryCloudSceneID = RecCloudScene;
231                    PreviousHistoryEyeSceneID = RecEyeScene;
232                    if (PreviousHistoryEyeSceneID <= 2) {
233                        FoundEyeSceneTF = true;
234                    }
235                    if ((PreviousHistoryCloudSceneID == 4) && (PreviousHistoryEyeSceneID == 3)) {
236                        FoundEyeSceneTF = false;
237                    }
238                    PreviousValidHistoryTnoValue = PreviousHistoryTnoValue;
239                    LastRule9Value = RecRule9;
240                    if (PreviousHistoryTnoValue > MaximumRule9Value) {
241                        MaximumRule9Value = PreviousHistoryTnoValue;
242                    }
243                } else {
244                    if (!LandCheckTF) {
245                        /* if over land for > 12 hours, turn off FoundEyeSceneTF */
246                        if ((HistoryRecTime - LastValidHistoryRecTime) > 0.5) {
247                            FoundEyeSceneTF = false;
248                            PreviousHistoryTnoValue = PreviousValidHistoryTnoValue
249                                    - (1.0 * (HistoryRecTime - LastValidHistoryRecTime));
250                            /*
251                             * printf("PreviousValidHistoryTnoValue=%f
252                             * deltatime=%f PreviousHistoryTnoValue=%f\n",
253                             * PreviousValidHistoryTnoValue,
254                             * HistoryRecTime-LastValidHistoryRecTime,
255                             * PreviousHistoryTnoValue);
256                             */
257                        }
258                    }
259                }
260            }
261            /* check for large break in history file */
262            if (!FoundHistoryRecMinus12hrTF) {
263                PreviousHistoryTnoValueMinus12hrs = PreviousHistoryTnoValue;
264            }
265        }
266
267        /*
268         * System.out.printf("FoundHistoryRecMinus12hrTF=%b\n",
269         * FoundHistoryRecMinus12hrTF);
270         */
271        /*
272         * System.out.printf("PreviousHistoryTnoValueMinus12hrs=%f\n",
273         * PreviousHistoryTnoValueMinus12hrs);
274         */
275
276        int EyeSceneIDValue;
277        double EyeSceneFactorC = 0.0;
278        double EyeSceneFactorE = 0.0;
279        double EyeSceneFactorA = 1.0 - ((EyeFFTValue - 2) * 0.1);
280        double EyeSceneFactorB = -(EyeBDCategoryFloat * 0.5);
281
282        if (EyeStdvValue > 10.0) {
283            EyeSceneFactorC = 0.50;
284        }
285        double EyeSceneFactorD = (EyeCloudBDCategoryFloatDiff * 0.25)
286                + (EyeCloudCWBDCategoryFloatDiff * 0.50);
287
288        /* System.out.printf("EyeSceneFactorD=%f\n",EyeSceneFactorD); */
289        /*
290         * System.out.printf("MaximumRule9Value=%f EyeSceneFactorE=%f\n",
291         * MaximumRule9Value,EyeSceneFactorE);
292         */
293
294        if ((FoundHistoryRecMinus12hrTF) && (PreviousHistoryEyeSceneID < 3)
295                && (MaximumRule9Value > 5.0)) {
296            EyeSceneFactorC = EyeSceneFactorC + 0.25; /*
297                                                       * changed from
298                                                       * EyeSceneFactorE
299                                                       */
300        }
301        /* System.out.printf("EyeSceneFactorE=%f\n",EyeSceneFactorE); */
302        if (PreviousHistoryTnoValueMinus12hrs <= 4.5) {
303            EyeSceneFactorE = Math.max(-1.0, PreviousHistoryTnoValueMinus12hrs - 4.5);
304        }
305
306        /*
307         * System.out.printf(
308         * "PreviousHistoryTnoValueMinus12hrs=%f  EyeSceneFactorE=%f\n",
309         * PreviousHistoryTnoValueMinus12hrs,EyeSceneFactorE);
310         */
311
312        if ((LastRule9Value > 0) && (PreviousHistoryTnoValue < 4.0)) {
313            EyeSceneFactorE = EyeSceneFactorE - 0.5;
314        }
315        /* System.out.printf("EyeSceneFactorE=%f\n",EyeSceneFactorE); */
316        double EyeFactorTotal = EyeSceneFactorA + EyeSceneFactorB + EyeSceneFactorC
317                + EyeSceneFactorD + EyeSceneFactorE;
318        EyeSceneIDValue = 3; /* NO EYE */
319        if (EyeFactorTotal >= 0.50) {
320            EyeSceneIDValue = 0; /* EYE */
321        }
322
323        /*
324         * System.out.printf("EyeFactorTotal= %f  EyeSceneIDValue=%d \n",
325         * EyeFactorTotal,EyeSceneIDValue);
326         */
327
328        double EyeCDOSizeValue = 0.0;
329        /* System.out.printf("RMW SIZE=%f\n",RMWSize); */
330        if (RMWSize > 0.0) {
331            /* System.out.printf("Manually Entered RMW Size=%f\n",RMWSize); */
332            History.IRCurrentRecord.rmw = RMWSize;
333            EyeCDOSizeValue = RMWSize - 1.0; /* manually input eye size */
334        } else {
335            /* System.out.printf("Calculating RMW Size\n"); */
336            double LocalValue[] = Data.CalcRMW();
337            double RadiusMaxWind = LocalValue[0];
338            /* System.out.printf("Calculated RMW Size=%f\n",RadiusMaxWind); */
339            History.IRCurrentRecord.rmw = RadiusMaxWind;
340        }
341
342        /* LARGE EYE CHECKS */
343        double LargeEyeRadius = LARGE_EYE_RADIUS;
344        if ((EyeSceneIDValue == 0) && (EyeCDOSizeValue >= LargeEyeRadius)) {
345            EyeSceneIDValue = 2; /* large eye */
346        }
347
348        /* NEW CLOUD SCENE THRESHOLD DETERMINATION */
349        boolean ShearSceneTF = false;
350        boolean IrregularCDOSceneTF = false;
351        boolean CurvedBandSceneTF = true;
352        boolean CurvedBandBDGrayShadeTF = true;
353        boolean CurvedBandBDBlackWhiteTF = false;
354        boolean EmbeddedCenterCheckTF = false;
355        boolean EmbeddedCenterSceneTF = false;
356
357        double CloudSceneFactorC = 0.0;
358        double CloudSceneFactorD = 0.5;
359        double CloudSceneFactorE = 0.0;
360        double CloudSceneFactorA = CloudCWBDCategoryFloat * 0.25;
361        double CloudSceneFactorB = CloudBDCategoryFloat * 0.25;
362        if (CloudFFTValue <= 2) {
363            CloudSceneFactorC = Math.min(1.50, CloudCWBDCategoryFloat * 0.25);
364        }
365        if (PreviousHistoryCloudSceneID >= 3) {
366            CloudSceneFactorD = -0.50;
367        }
368
369        /*
370         * System.out.printf(
371         * "CloudCWBDCategoryFloat=%f PreviousHistoryTnoValueMinus12hrs=%f\n",
372         * CloudCWBDCategoryFloat,PreviousHistoryTnoValueMinus12hrs);
373         */
374
375        if (CloudCWBDCategoryFloat > 2.0) {
376            if (PreviousHistoryTnoValueMinus12hrs >= 2.5) {
377                if (EyeSceneIDValue == 0) {
378                    CloudSceneFactorE = Math.min(1.00, PreviousHistoryTnoValueMinus12hrs - 2.5);
379                }
380                if (PreviousHistoryTnoValueMinus12hrs >= 3.5) {
381                    CloudSceneFactorE = CloudSceneFactorE + 1.00;
382                }
383            }
384            if ((FoundHistoryRecMinus12hrTF) && (FoundEyeSceneTF))
385                CloudSceneFactorE = CloudSceneFactorE + 1.25;
386        }
387        double CloudFactorTotal = CloudSceneFactorA + CloudSceneFactorB + CloudSceneFactorC
388                + CloudSceneFactorD + CloudSceneFactorE;
389        if (CloudFactorTotal < 0.0) {
390            ShearSceneTF = true; /* SHEAR */
391        }
392        if (CloudFactorTotal >= 0.00) {
393            CurvedBandSceneTF = true; /* CURVED BAND (gray) */
394        }
395        if (CloudFactorTotal >= 1.00) {
396            CurvedBandSceneTF = true; /* CURVED BAND (gray) */
397            /* check for irregular CDO */
398            if ((EyeCloudTemperatureDiff2 < 0.0) && (CloudSymmetryValue > 40.0)) {
399                IrregularCDOSceneTF = true; /* IRREGULAR CDO */
400            }
401        }
402        if ((CloudFactorTotal >= 2.00) && (CloudFactorTotal < 3.00)) {
403            CurvedBandSceneTF = true; /* CURVED BAND (gray) */
404            /* check for irregular CDO */
405            if ((EyeCloudTemperatureDiff2 < 0.0) && (CloudSymmetryValue > 30.0)) {
406                IrregularCDOSceneTF = true; /* IRREGULAR CDO */
407            }
408            if (CloudCWBDCategory >= 3) {
409                /* if xcwt>3.0 try black/white CB check */
410                if ((CloudBDDifference > 0) && (CloudTemperatureDifference < -8.0)) {
411                    CurvedBandBDGrayShadeTF = false; /* CURVED BAND (b/w) */
412                    CurvedBandBDBlackWhiteTF = true;
413                }
414                /* check for large/ragged eye */
415                if ((EyeSceneIDValue == 0)
416                        || ((EyeBDCategoryFloat > 1.00) && (EyeCloudBDCategoryDifference >= 2.00))) {
417                    CurvedBandSceneTF = false; /* EYE */
418                }
419                /* check for CDO */
420                if ((CloudBDCategoryFloatDiff <= 0.0) && (EyeCloudCWBDCategoryFloatDiff < 1.00)) {
421                    CurvedBandSceneTF = false; /* CDO */
422                }
423            }
424        }
425        if (CloudFactorTotal >= 3.00) {
426            CurvedBandSceneTF = false; /* CDO */
427            /* check for irregular CDO */
428            if ((CloudBDDifference < 0) && (CloudTemperatureDifference > 8.0)
429                    && (CloudSymmetryValue > 30.0)) {
430                IrregularCDOSceneTF = true; /* IRREGULAR CDO */
431                CurvedBandSceneTF = true;
432            }
433        }
434        /* EMBEDDED CENTER CHECK */
435        if ((CloudTemperature < CloudCWTemperature) && (CloudCWTemperature < EyeTemperature)) {
436            EmbeddedCenterCheckTF = true;
437        }
438        if ((!CurvedBandSceneTF) && (EmbeddedCenterCheckTF)) {
439            TemperatureValue = BDCurve_Points[CloudCWBDCategory + 1] + 273.16;
440            double ReturnValues[] = Scene.adt_logspiral(StormLatitude, StormLongitude,
441                    TemperatureValue, 1);
442            LogSpiralAmount = (int) ReturnValues[0];
443            if ((LogSpiralAmount >= 8) && (LogSpiralAmount < 20)) {
444                EmbeddedCenterSceneTF = true;
445            }
446
447            /*
448             * System.out.printf(
449             * " EMBDD : CloudCWBDCategory=%d LogSpiralAmount=%d \n",
450             * CloudCWBDCategory,LogSpiralAmount);
451             */
452
453        }
454
455        /*
456         * System.out.printf(
457         * "CloudFactorTotal= %f  ShearSceneTF=%b CurvedBandSceneTF=%b CurvedBandBDGrayShadeTF=%b IrregularCDOSceneTF=%b \n"
458         * ,
459         * CloudFactorTotal,ShearSceneTF,CurvedBandSceneTF,CurvedBandBDGrayShadeTF
460         * ,IrregularCDOSceneTF);
461         */
462
463        /*
464         * System.out.printf(
465         * "%9s %6d %4.1f %4.1f %2d %2d %5.1f %5.1f  %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f  %2d %2d %4.1f %4.1f  %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f   %6.2f %7.2f %3.1f  \n"
466         * ,ImageDateString,ImageTime,
467         * CloudBDCategoryFloat,CloudCWBDCategoryFloat,CloudFFTValue,
468         * CloudBDDifference,CloudTemperatureDifference,CloudSymmetryValue,
469         * CloudSceneFactorA,CloudSceneFactorB,0.0,CloudSceneFactorC,
470         * CloudSceneFactorD,CloudSceneFactorE,CloudFactorTotal,
471         * EyeFFTValue,EyeCloudBDCategoryDifference
472         * ,EyeBDCategoryFloat,EyeStdvValue,
473         * EyeSceneFactorA,EyeSceneFactorB,EyeSceneFactorC,EyeSceneFactorD,
474         * EyeSceneFactorE,EyeFactorTotal,StormLatitude,StormLongitude,
475         * PreviousHistoryTnoValue);
476         */
477
478        /* CLASSIFY CLOUD REGION */
479        int CurvedBandBDCategory = 0;
480        int CurvedBandBDAmount = 0;
481        int CurvedBandBDMaxAmount = 0;
482        int CloudSceneIDValue = -99;
483        boolean FoundCurvedBandSceneTF = false;
484        double ShearDistance = -99.0;
485
486        /*
487         * System.out.printf(
488         * "CurvedbandsceneTF=%b  IrregularCDOTF=%b  ShearSceneTF=%b \n"
489         * ,CurvedBandSceneTF,IrregularCDOSceneTF,ShearSceneTF);
490         */
491        if (CurvedBandSceneTF) {
492            if (ShearSceneTF) {
493                EyeSceneIDValue = 3; /* NO EYE */
494                CloudSceneIDValue = 4; /* SHEAR */
495                TemperatureValue = ((BDCurve_Points[2] + BDCurve_Points[3]) / 2.0) + 273.16;
496                ShearDistance = adt_cdoshearcalc(StormLatitude, StormLongitude, TemperatureValue, 3);
497                EyeCDOSizeValue = Math.max(4.0, ShearDistance);
498            } else if (IrregularCDOSceneTF) {
499                EyeSceneIDValue = 3; /* NO EYE */
500                CloudSceneIDValue = 2; /* IRREGULAR CDO */
501            } else {
502                FoundCurvedBandSceneTF = false;
503                if (CurvedBandBDGrayShadeTF) {
504                    /* perform Curved Band analysis */
505                    XInc = 4; /* start with LIGHT GRAY */
506                    while ((XInc >= 2) && (!FoundCurvedBandSceneTF)) {
507                        TemperatureValue = BDCurve_Points[XInc] + 273.16;
508                        double ReturnValues[] = Scene.adt_logspiral(StormLatitude, StormLongitude,
509                                TemperatureValue, 1);
510                        LogSpiralAmount = (int) ReturnValues[0];
511                        if ((LogSpiralAmount >= 8) || (XInc == 2)) {
512                            /* 10 = .375% -- 10 ==> 9 arcs of 15 degrees */
513                            if (LogSpiralAmount > 25) {
514                                if (XInc == 4) {
515                                    CurvedBandBDGrayShadeTF = false;
516                                    CurvedBandBDBlackWhiteTF = true;
517                                    /*
518                                     * following line to exit out of While
519                                     * statement
520                                     */
521                                    FoundCurvedBandSceneTF = true;
522                                } else {
523                                    XInc = 0;
524                                }
525                            } else {
526                                if ((XInc == 2) && (LogSpiralAmount < 7)) {
527                                    /* 7 = .25% -- 10 ==> 6 arcs of 15 degrees */
528                                    /* probably shear */
529                                    FoundCurvedBandSceneTF = false;
530                                    CurvedBandBDBlackWhiteTF = false;
531                                    ShearSceneTF = true;
532                                    /* gross error check... added 08/15/13 */
533                                    if ((EyeBDCategoryFloat > 1.5) || (CloudBDCategoryFloat > 2.5)) {
534                                        ShearSceneTF = false;
535                                        IrregularCDOSceneTF = true;
536                                    }
537                                    /*
538                                     * following line to exit out of While
539                                     * statement
540                                     */
541                                    XInc--;
542                                } else {
543                                    FoundCurvedBandSceneTF = true;
544                                }
545                            }
546                        } else {
547                            XInc--;
548                        }
549                    }
550                }
551                if (CurvedBandBDBlackWhiteTF) {
552                    /* try BLACK and WHITE rings */
553                    FoundCurvedBandSceneTF = false;
554                    CurvedBandSceneTF = false;
555                    XInc = 6;
556                    while ((XInc > 4) && (!FoundCurvedBandSceneTF)) {
557                        TemperatureValue = BDCurve_Points[XInc] + 273.16;
558                        double ReturnValues[] = Scene.adt_logspiral(StormLatitude, StormLongitude,
559                                TemperatureValue, 1);
560                        LogSpiralAmount = (int) ReturnValues[0];
561                        if ((LogSpiralAmount >= 9) && (LogSpiralAmount <= 25)) {
562                            FoundCurvedBandSceneTF = true;
563                            /* EmbeddedCenterSceneTF = true; needed here? */
564                        } else {
565                            XInc--;
566                        }
567                    }
568                }
569                if (FoundCurvedBandSceneTF) {
570                    /* found curved band scenes */
571                    CurvedBandBDCategory = XInc;
572                    CurvedBandBDAmount = LogSpiralAmount;
573                    EyeSceneIDValue = 3; /* NO EYE */
574                    CloudSceneIDValue = 3; /* CURVED BAND */
575                    /*
576                     * search for max curved band analysis location w/in
577                     * 1-degree box
578                     */
579                    TemperatureValue = BDCurve_Points[CurvedBandBDCategory] + 273.16;
580                    boolean CBSearchTF_Global = true;
581                    if (CBSearchTF_Global) { /* need global variable here */
582                        double ReturnValues[] = Scene.adt_logspiral(StormLatitude, StormLongitude,
583                                TemperatureValue, 2);
584                        CurvedBandBDMaxAmount = (int) ReturnValues[0];
585                        CurvedBandBDMaxLatitude = ReturnValues[1];
586                        CurvedBandBDMaxLongitude = ReturnValues[2];
587                        /*
588                         * System.out.printf("max amounts %d %f %f\n",
589                         * CurvedBandBDMaxAmount
590                         * ,CurvedBandBDMaxLatitude,CurvedBandBDMaxLongitude);
591                         */
592                    }
593                } else {
594                    /*
595                     * did not find curved band scenes, mark as non-eye/eye
596                     * scene
597                     */
598                    CloudSceneIDValue = 0;
599                    CurvedBandSceneTF = false;
600                    EmbeddedCenterSceneTF = false;
601                }
602            }
603        }
604        /*
605         * System.out.printf(
606         * "CurvedbandsceneTF=%b  IrregularCDOTF=%b  ShearSceneTF=%b EmbeddedCenterTF=%b \n"
607         * ,
608         * CurvedBandSceneTF,IrregularCDOSceneTF,ShearSceneTF,EmbeddedCenterSceneTF
609         * );
610         */
611        if (!CurvedBandSceneTF) {
612            if (ShearSceneTF) {
613                /* shear scene */
614                EyeSceneIDValue = 3; /* NO EYE */
615                CloudSceneIDValue = 4; /* SHEAR */
616                TemperatureValue = ((BDCurve_Points[2] + BDCurve_Points[3]) / 2.0) + 273.16;
617                ShearDistance = adt_cdoshearcalc(StormLatitude, StormLongitude, TemperatureValue, 3);
618                EyeCDOSizeValue = Math.max(4.0, ShearDistance);
619            } else {
620                CloudSceneIDValue = 0; /* UNIFORM */
621                if (EmbeddedCenterSceneTF) {
622                    CloudSceneIDValue = 1; /* EMBEDDED CENTER */
623                }
624                /* added 08/15/13 */
625                if (IrregularCDOSceneTF) {
626                    CloudSceneIDValue = 2; /* Irregular CDO */
627                }
628                /* PINHOLE EYE TEST */
629                /*
630                 * System.out.printf("EyeFactorTotal=%f\n",
631                 * "EyeSceneIDValue=%d EyeCloudBDCategoryDifference=%d ",
632                 * "EyeFFTValue=%d CloudCWBDCategoryFloat=%f \n",
633                 * "CloudSceneIDValue=%d CloudFFTValue=%d ",
634                 * "PreviousHistoryTnoValueMinus12hrs=%f\n",EyeFactorTotal,
635                 * EyeSceneIDValue,EyeCloudBDCategoryDifference,EyeFFTValue,
636                 * CloudCWBDCategoryFloat,CloudSceneIDValue,CloudFFTValue,
637                 * PreviousHistoryTnoValueMinus12hrs);
638                 */
639                if ((RMWSize > 0.0) && (RMWSize < 12.0)) {
640                    EyeSceneIDValue = 1; /* PINHOLE EYE CHECK */
641                }
642                if ((EyeFactorTotal > -0.25) && (EyeFactorTotal < 1.50)
643                        && (EyeCloudBDCategoryDifference >= 2) && (EyeFFTValue <= 2)
644                        && (CloudCWBDCategoryFloat > 6.0) && (CloudSceneIDValue <= 1)
645                        && (CloudFFTValue <= 4) && (PreviousHistoryTnoValueMinus12hrs >= 3.5)) {
646                    EyeSceneIDValue = 1; /* PINHOLE EYE CHECK */
647                }
648            }
649        }
650        double CDOSize = -999.0;
651        /*
652         * System.out.printf("cloudsceneID=%d eyesceneID=%d\n",CloudSceneIDValue,
653         * EyeSceneIDValue);
654         */
655        if ((CloudSceneIDValue <= 2) && (EyeSceneIDValue == 3)) {
656            /* for CDO TESTS */
657            for (XInc = 2; XInc <= 6; XInc++) { /* DG,MG,LG,B,W */
658                TemperatureValue = BDCurve_Points[XInc] + 273.16;
659                /*
660                 * System.out.printf(
661                 * "LatitudeValue=%f LongitudeValue=%f TemperatureValue=%f\n"
662                 * ,StormLatitude,StormLongitude,TemperatureValue);
663                 */
664                CDOSize = adt_cdoshearcalc(StormLatitude, StormLongitude, TemperatureValue, 1);
665                /*
666                 * System.out.printf("CDO : XInc=%d  CDOSize=%f  CDOSize/111=%f  \n"
667                 * ,XInc,CDOSize,CDOSize/111.0);
668                 */
669                if (XInc == 2) {
670                    EyeCDOSizeValue = CDOSize;
671                }
672            }
673        }
674
675        /*
676         * System.out.printf(
677         * "eyescene=%d cloudscene=%d eyecdosize=%f ringcb=%d ringcbval=%d\n",
678         * EyeSceneIDValue
679         * ,CloudSceneIDValue,EyeCDOSizeValue,CurvedBandBDCategory
680         * ,CurvedBandBDAmount);
681         */
682        /*
683         * System.out.printf("CBMAX :ringcbval=%d ringcb=%d ringcbval=%d\n",
684         * CurvedBandBDMaxAmount,CurvedBandBDCategory,CurvedBandBDAmount);
685         */
686
687        /*
688         * System.out.printf("EyeScene=%d CloudScene=%d EyeCDOSize=%f \n",
689         * EyeSceneIDValue,CloudSceneIDValue,EyeCDOSizeValue);
690         */
691        History.IRCurrentRecord.eyescene = EyeSceneIDValue;
692        History.IRCurrentRecord.cloudscene = CloudSceneIDValue;
693        History.IRCurrentRecord.eyesceneold = -1;
694        History.IRCurrentRecord.cloudsceneold = -1;
695        History.IRCurrentRecord.eyecdosize = EyeCDOSizeValue;
696        History.IRCurrentRecord.ringcb = CurvedBandBDCategory;
697        History.IRCurrentRecord.ringcbval = CurvedBandBDAmount;
698        History.IRCurrentRecord.ringcbvalmax = CurvedBandBDMaxAmount;
699        History.IRCurrentRecord.ringcbvalmaxlat = CurvedBandBDMaxLatitude;
700        History.IRCurrentRecord.ringcbvalmaxlon = CurvedBandBDMaxLongitude;
701        History.IRCurrentRecord.mwscore = Env.MWScore;
702        History.IRCurrentRecord.mwdate = Env.MWJulianDate;
703        History.IRCurrentRecord.mwtime = Env.MWHHMMSSTime;
704
705    }
706
707    /**
708     * Determine storm location using 10^ Log-spiral analysis.
709     *
710     * <p>
711     * Algorithm will attempt to match the spiral with the image pixels at or
712     * below the threshold temperature based on BD-enhancement curve values.
713     * </p>
714     *
715     * @param InputLatitude
716     *            Center latitude of analysis grid
717     * @param InputLongitude
718     *            Center longitude of analysis grid
719     * @param TemperatureThreshold
720     *            Temperature threshold value
721     * @param AnalysisTypeIDValue
722     *            1=search at single point 2=search over 2^box
723     *
724     * @return Array of three double values. In order, they are:
725     *         <ol>
726     *         <li>SpiralArcLatitude - best latitude location from analysis.</li>
727     *         <li>SpiralArcLongitude - best longitude location from analysis.</li>
728     *         <li>SpiralArcDistance - number of consecutive arcs through which
729     *         spiral passes</li>
730     *         </ol>
731     */
732    public static double[] adt_logspiral(double InputLatitude, double InputLongitude,
733            double TemperatureThreshold, int AnalysisTypeIDValue) {
734        int XInc, YInc, ZInc;
735        int SpiralArcDistance = -99;
736        int SearchLatitudeMaxInteger, SearchLatitudeMinInteger;
737        int SearchLongitudeMaxInteger, SearchLongitudeMinInteger;
738        double SearchLatitudeMaximum, SearchLatitudeMinimum;
739        double SearchLongitudeMaximum, SearchLongitudeMinimum;
740        double SpiralArcLatitude = -999.99;
741        double SpiralArcLongitude = -999.99;
742        float ValidPixelLatitudeArray[] = new float[40000];
743        float ValidPixelLongitudeArray[] = new float[40000];
744        float ValidPixelTemperatureArray[] = new float[40000];
745
746        double ImageResolution = Data.GetCurrentImageResolution();
747        double DistanceDifferenceMaximumKM = ImageResolution + (ImageResolution / 2.0);
748        int IncAddVal = (ImageResolution > RING_WIDTH) ? 1
749                : ((int) (RING_WIDTH - ImageResolution + 1.0));
750
751        if (AnalysisTypeIDValue == 2) {
752            /* search over 2.0 degree box */
753            SearchLatitudeMaximum = InputLatitude + 1.0;
754            SearchLatitudeMinimum = InputLatitude - 1.0;
755            SearchLongitudeMaximum = InputLongitude + 1.0;
756            SearchLongitudeMinimum = InputLongitude - 1.0;
757            SearchLatitudeMaxInteger = (int) (SearchLatitudeMaximum * 100.0);
758            SearchLatitudeMinInteger = (int) (SearchLatitudeMinimum * 100.0);
759            SearchLongitudeMaxInteger = (int) (SearchLongitudeMaximum * 100.0);
760            SearchLongitudeMinInteger = (int) (SearchLongitudeMinimum * 100.0);
761        } else {
762            /* search at a single point */
763            SearchLatitudeMaxInteger = (int) (InputLatitude * 100.0);
764            SearchLatitudeMinInteger = (int) (InputLatitude * 100.0);
765            SearchLongitudeMaxInteger = (int) (InputLongitude * 100.0);
766            SearchLongitudeMinInteger = (int) (InputLongitude * 100.0);
767        }
768
769        /* allocate memory, if necessary */
770        if (IRImageXSize == -1) {
771            IRImageXSize = Data.GetCurrentImageXSize();
772            IRImageYSize = Data.GetCurrentImageYSize();
773            IRImageLatitudeArrayLocal = Data.GetCurrentImageLatitudeArray();
774            IRImageLongitudeArrayLocal = Data.GetCurrentImageLongitudeArray();
775            IRImageTemperatureArrayLocal = Data.GetCurrentImageTemperatureArray();
776        }
777
778        /* System.out.printf("temperature threshold=%f\n",TemperatureThreshold); */
779        /* initialize arrays */
780        int ValidPointCounter = 0;
781        for (YInc = 0; YInc < IRImageYSize; YInc = YInc + IncAddVal) {
782            for (XInc = 0; XInc < IRImageXSize; XInc = XInc + IncAddVal) {
783                if (IRImageTemperatureArrayLocal[YInc][XInc] <= TemperatureThreshold) {
784                    ValidPixelLatitudeArray[ValidPointCounter] = IRImageLatitudeArrayLocal[YInc][XInc];
785                    ValidPixelLongitudeArray[ValidPointCounter] = IRImageLongitudeArrayLocal[YInc][XInc];
786                    ValidPixelTemperatureArray[ValidPointCounter] = IRImageTemperatureArrayLocal[YInc][XInc];
787                    ValidPointCounter++;
788                }
789            }
790        }
791        /* System.out.printf("Valid Point Counter=%d\n",ValidPointCounter); */
792
793        int ArcSkipCounter;
794        int ThresholdCounter;
795        int SpiralConsecutiveArcCounter;
796        int SpiralConsecutiveArcMaximum;
797        double SpiralStartMinimumDistance;
798        double SearchIncrementLatitude = 0.0;
799        double SearchIncrementLongitude = 0.0;
800        double SpiralArcBestRotationAngleValue = 0.0;
801        double ArcAngleTheta_Radians;
802        double RadialDistanceKM;
803        double FinalArcAngleTheta, FinalArcAngleThetaPlus180;
804        double RadiansValue = 57.29578; /* degree to radians conversion value */
805        double SpiralConstantAValue = 25.0; /* 10^ log spiral distance constant */
806        double SpiralConstantBValue = 10.0 / RadiansValue; /*
807                                                            * 10^ log spiral
808                                                            * increase
809                                                            */
810        double LatitudeDifference;
811        double LongitudeDifference;
812
813        for (XInc = SearchLatitudeMinInteger; XInc <= SearchLatitudeMaxInteger; XInc = XInc + 20) {
814            SearchIncrementLatitude = (double) XInc / 100.0;
815            /* loop through y-axis/lines of analysis grid box */
816            for (YInc = SearchLongitudeMinInteger; YInc <= SearchLongitudeMaxInteger; YInc = YInc + 20) {
817                SearchIncrementLongitude = (double) YInc / 100.0;
818                ArcSkipCounter = 0;
819                /* determine distance from each point in box to current location */
820                if (AnalysisTypeIDValue == 2) {
821                    SpiralStartMinimumDistance = 12.0;
822                    for (ZInc = 0; ZInc < ValidPointCounter; ZInc++) {
823                        double LocalValue[] = Functions.distance_angle(SearchIncrementLatitude,
824                                SearchIncrementLongitude, ValidPixelLatitudeArray[ZInc],
825                                ValidPixelLongitudeArray[ZInc], 1);
826                        double DistanceValue = LocalValue[0];
827                        if (DistanceValue <= SpiralStartMinimumDistance) {
828                            /*
829                             * if the lat/lon point is too close to cold cloud
830                             * tops, do not calculate log spiral at this point.
831                             * Trying to eliminate "false" arc locations by
832                             * forcing the system to use some of the arc points
833                             * on the spiral away from the start of the spiral
834                             * (were getting "false echos" without this".
835                             */
836                            ArcSkipCounter = 1;
837                            break;
838                        }
839                    }
840                }
841
842                int SpiralArcMaximumValue = 0;
843                int SpiralArcMaximumValueRotationFactor = 0;
844                /*
845                 * if arc location passes analysis above, proceed with placement
846                 * of spiral
847                 */
848
849                if (ArcSkipCounter == 0) {
850                    /*
851                     * rotate the arc spiral thru entire revolution at 30^
852                     * interval
853                     */
854                    for (int RotationFactor = 0; RotationFactor <= 330; RotationFactor = RotationFactor + 30) {
855                        SpiralConsecutiveArcCounter = 0;
856                        SpiralConsecutiveArcMaximum = 0;
857
858                        /*
859                         * calculate position of each point on spiral from 0 to
860                         * 540^
861                         */
862                        for (int ArcAngleTheta = 0; ArcAngleTheta <= 540; ArcAngleTheta = ArcAngleTheta + 15) {
863                            ArcAngleTheta_Radians = (double) ArcAngleTheta / RadiansValue;
864                            RadialDistanceKM = SpiralConstantAValue
865                                    * Math.exp((SpiralConstantBValue * ArcAngleTheta_Radians));
866                            FinalArcAngleTheta = (double) ArcAngleTheta + (double) RotationFactor;
867                            if (SearchIncrementLatitude < 0.0) {
868                                FinalArcAngleTheta = (double) (-1 * ArcAngleTheta)
869                                        + (double) RotationFactor;
870                            }
871                            FinalArcAngleThetaPlus180 = FinalArcAngleTheta + 180.0;
872                            double LocalValue2[] = Functions.distance_angle2(
873                                    SearchIncrementLatitude, SearchIncrementLongitude,
874                                    RadialDistanceKM, FinalArcAngleThetaPlus180);
875                            double SearchGuessLatitude = LocalValue2[0];
876                            double SearchGuessLongitude = LocalValue2[1];
877                            ThresholdCounter = 0;
878                            for (ZInc = 0; ZInc < ValidPointCounter; ZInc++) {
879                                LatitudeDifference = Math.abs(SearchGuessLatitude
880                                        - ValidPixelLatitudeArray[ZInc]);
881                                LongitudeDifference = Math.abs(SearchGuessLongitude
882                                        - ValidPixelLongitudeArray[ZInc]);
883                                /*
884                                 * if a point is within 0.1^ latitude/longitude
885                                 * determine distance
886                                 */
887                                if ((LatitudeDifference <= 0.1) && (LongitudeDifference <= 0.1)) {
888                                    double LocalValue3[] = Functions.distance_angle(
889                                            SearchGuessLatitude, SearchGuessLongitude,
890                                            ValidPixelLatitudeArray[ZInc],
891                                            ValidPixelLongitudeArray[ZInc], 1);
892                                    double DistanceValue3 = LocalValue3[0];
893                                    /*
894                                     * if distance from spiral point is within
895                                     * 6km from an accepted temperature
896                                     * threshold point, count it
897                                     */
898                                    if (DistanceValue3 <= DistanceDifferenceMaximumKM) {
899                                        ThresholdCounter++;
900                                    }
901                                }
902                            }
903                            /*
904                             * if there are 4 or more threshold points
905                             * associated with each spiral point, count within
906                             * consecutive spiral point counter
907                             */
908                            if (ThresholdCounter >= 4) {
909                                SpiralConsecutiveArcCounter++;
910                                /*
911                                 * save spiral that has maximum consecutive
912                                 * spiral counts for each rotation though 360^
913                                 * at each center location
914                                 */
915                                if (SpiralConsecutiveArcCounter > SpiralConsecutiveArcMaximum) {
916                                    SpiralConsecutiveArcMaximum = SpiralConsecutiveArcCounter;
917                                }
918                            } else {
919                                SpiralConsecutiveArcCounter = 0;
920                            }
921                            /*
922                             * if this spiral has the greatest number of
923                             * consecutive spiral points, save the location and
924                             * number of points
925                             */
926                            if (SpiralConsecutiveArcMaximum > SpiralArcMaximumValue) {
927                                SpiralArcMaximumValue = SpiralConsecutiveArcMaximum;
928                                SpiralArcMaximumValueRotationFactor = RotationFactor;
929                            }
930                        }
931                    } /* RotationFactor loop */
932                    if (SpiralArcMaximumValue > SpiralArcDistance) {
933                        SpiralArcDistance = SpiralArcMaximumValue;
934                        SpiralArcLatitude = SearchIncrementLatitude;
935                        SpiralArcLongitude = SearchIncrementLongitude;
936                        SpiralArcBestRotationAngleValue = SpiralArcMaximumValueRotationFactor;
937                    }
938                } /* ArcSkipCounter if */
939            } /* YInc loop */
940        } /* XInc loop */
941
942        /* load array for best spiral band */
943        for (int ArcAngleTheta = 0; ArcAngleTheta <= 540; ArcAngleTheta = ArcAngleTheta + 15) {
944            ArcAngleTheta_Radians = (double) ArcAngleTheta / RadiansValue;
945            RadialDistanceKM = SpiralConstantAValue
946                    * Math.exp((SpiralConstantBValue * ArcAngleTheta_Radians));
947            FinalArcAngleTheta = (double) ArcAngleTheta + (double) SpiralArcBestRotationAngleValue;
948            if (SearchIncrementLatitude < 0.0) {
949                FinalArcAngleTheta = (double) (-1 * ArcAngleTheta)
950                        + (double) SpiralArcBestRotationAngleValue;
951            }
952            FinalArcAngleThetaPlus180 = FinalArcAngleTheta + 180.0;
953            /* load array for external plotting of spiral band */
954            /* SpiralBandPoints_Global[0][TemporaryCounter]=SearchGuessLatitude; */
955            /*
956             * SpiralBandPoints_Global[1][TemporaryCounter]=SearchGuessLongitude;
957             */
958        }
959
960        return new double[] { (double) SpiralArcDistance, SpiralArcLatitude, SpiralArcLongitude };
961
962    }
963
964    /**
965     * Determine eye size or shear distance for a given scene.
966     *
967     * @param InputLatitude
968     *            Center latitude of analysis grid
969     * @param InputLongitude
970     *            Center longitude of analysis grid
971     * @param TemperatureThreshold
972     *            Temperature threshold value to be used
973     * @param AnalysisTypeIDValue
974     *            Analysis type (1-cdo size,2-eye size,3-shear distance)
975     *
976     * @return eye/cdo radius or shear distance
977     */
978    public static double adt_cdoshearcalc(double InputLatitude, double InputLongitude,
979            double TemperatureThreshold, int AnalysisTypeIDValue) {
980        /* maximum distance constant value */
981        double ANGLEMAXDIFFERENCE = 15.0;
982        double RadiusOrShear = -99.0;
983        double Value3 = MANUAL_EYE_RADIUS + RING_WIDTH;
984        double PixelLatitudeArray[] = new double[40000];
985        double PixelLongitudeArray[] = new double[40000];
986        double PixelTemperatureArray[] = new double[40000];
987
988        int XInc, YInc, ZInc;
989        int PointCounter = 0;
990        int ValidRadiiCounter = 4;
991        double DistanceValue, AngleValue;
992        double SymmValue;
993
994        double RadiiLength1 = 300.0;
995        double RadiiLength2 = 300.0;
996        double RadiiLength3 = 300.0;
997        double RadiiLength4 = 300.0;
998        double MaxDistanceValue = 0.0;
999
1000        /* allocate memory, if necessary */
1001        if (IRImageXSize == -1) {
1002            IRImageXSize = Data.GetCurrentImageXSize();
1003            IRImageYSize = Data.GetCurrentImageYSize();
1004            IRImageLatitudeArrayLocal = Data.GetCurrentImageLatitudeArray();
1005            IRImageLongitudeArrayLocal = Data.GetCurrentImageLongitudeArray();
1006            IRImageTemperatureArrayLocal = Data.GetCurrentImageTemperatureArray();
1007        }
1008
1009        if (AnalysisTypeIDValue == 1) {
1010            /* CDO size determination - RETURNS RADIUS */
1011            for (YInc = 0; YInc < IRImageYSize; YInc++) {
1012                for (XInc = 0; XInc < IRImageXSize; XInc++) {
1013                    if (IRImageTemperatureArrayLocal[YInc][XInc] > TemperatureThreshold) {
1014                        PixelLatitudeArray[PointCounter] = IRImageLatitudeArrayLocal[YInc][XInc];
1015                        PixelLongitudeArray[PointCounter] = IRImageLongitudeArrayLocal[YInc][XInc];
1016                        PixelTemperatureArray[PointCounter] = IRImageTemperatureArrayLocal[YInc][XInc];
1017                        PointCounter++;
1018                    }
1019                }
1020            }
1021
1022            /*
1023             * System.out.printf("PointCounter=%d  numx*numy=%d\n",PointCounter,
1024             * IRImageYSize*IRImageXSize);
1025             */
1026            if (PointCounter < (IRImageYSize * IRImageXSize)) {
1027                for (ZInc = 0; ZInc < PointCounter; ZInc++) {
1028                    double LocalValue[] = Functions.distance_angle(InputLatitude, InputLongitude,
1029                            PixelLatitudeArray[ZInc], PixelLongitudeArray[ZInc], 1);
1030                    DistanceValue = LocalValue[0];
1031                    AngleValue = LocalValue[1];
1032                    if (DistanceValue > MaxDistanceValue)
1033                        MaxDistanceValue = DistanceValue;
1034                    /* determine size of CDO */
1035                    if (DistanceValue > MANUAL_EYE_RADIUS) {
1036                        if ((Math.abs(AngleValue - 45.0) <= ANGLEMAXDIFFERENCE)
1037                                && (DistanceValue < RadiiLength1)) {
1038                            RadiiLength1 = DistanceValue;
1039                        }
1040                        if ((Math.abs(AngleValue - 135.0) <= ANGLEMAXDIFFERENCE)
1041                                && (DistanceValue < RadiiLength2)) {
1042                            RadiiLength2 = DistanceValue;
1043                        }
1044                        if ((Math.abs(AngleValue - 225.0) <= ANGLEMAXDIFFERENCE)
1045                                && (DistanceValue < RadiiLength3)) {
1046                            RadiiLength3 = DistanceValue;
1047                        }
1048                        if ((Math.abs(AngleValue - 315.0) <= ANGLEMAXDIFFERENCE)
1049                                && (DistanceValue < RadiiLength4)) {
1050                            RadiiLength4 = DistanceValue;
1051                        }
1052                    }
1053                }
1054
1055                /*
1056                 * System.out.printf(
1057                 * "RadiiLength1=%f RadiiLength2=%fRadiiLength3=%f RadiiLength4=%f\n"
1058                 * , RadiiLength1,RadiiLength2,RadiiLength3,RadiiLength4);
1059                 */
1060
1061                if (RadiiLength1 < Value3) {
1062                    ValidRadiiCounter--;
1063                }
1064                if (RadiiLength2 < Value3) {
1065                    ValidRadiiCounter--;
1066                }
1067                if (RadiiLength3 < Value3) {
1068                    ValidRadiiCounter--;
1069                }
1070                if (RadiiLength4 < Value3) {
1071                    ValidRadiiCounter--;
1072                }
1073            } else {
1074                RadiiLength1 = 0.0;
1075                RadiiLength2 = 0.0;
1076                RadiiLength3 = 0.0;
1077                RadiiLength4 = 0.0;
1078            }
1079            if (ValidRadiiCounter < 3) {
1080                RadiusOrShear = 0.0;
1081            } else {
1082                RadiiLength1 = Math.min(RadiiLength1, MaxDistanceValue);
1083                RadiiLength2 = Math.min(RadiiLength2, MaxDistanceValue);
1084                RadiiLength3 = Math.min(RadiiLength3, MaxDistanceValue);
1085                RadiiLength4 = Math.min(RadiiLength4, MaxDistanceValue);
1086                RadiusOrShear = (RadiiLength1 + RadiiLength2 + RadiiLength3 + RadiiLength4) / 4.0;
1087                double Value1 = RadiiLength1 + RadiiLength3;
1088                double Value2 = RadiiLength2 + RadiiLength4;
1089                SymmValue = Value1 / Value2;
1090                SymmValue = Math.max(SymmValue, 1.0 / SymmValue);
1091            }
1092            /*
1093             * System.out.printf(
1094             * "\nPointCounter=%5d RadiiLength1=%5.1f RadiiLength2=%5.1f RadiiLength3=%5.1f RadiiLength4=%5.1f\n"
1095             * , PointCounter,
1096             * RadiiLength1,RadiiLength2,RadiiLength3,RadiiLength4);
1097             */
1098        }
1099
1100        if (AnalysisTypeIDValue == 3) {
1101            double ShearDistanceValue = -99.0;
1102            /*
1103             * need to implement entire odtauto.c library including remapping
1104             * RetErr=adt_shearbw(TemperatureValue,LatitudeValue,LongitudeValue,
1105             * &ShearDistanceValue);
1106             */
1107            RadiusOrShear = ShearDistanceValue;
1108        }
1109
1110        return RadiusOrShear;
1111    }
1112}