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.chooser;
030
031import java.io.File;
032import java.io.FilenameFilter;
033import java.io.IOException;
034import java.util.HashMap;
035import java.util.StringTokenizer;
036
037import javax.swing.filechooser.FileFilter;
038
039import org.slf4j.Logger;
040import org.slf4j.LoggerFactory;
041
042import ucar.nc2.Attribute;
043import ucar.nc2.NetcdfFile;
044
045import edu.wisc.ssec.mcidasv.data.hydra.JPSSUtilities;
046
047/**
048 * File filter to pass names which match format for Suomi NPP data.
049 * 
050 * @author tommyj
051 *
052 */
053
054public class SuomiNPPFilter extends FileFilter {
055        
056        private static final Logger logger =
057                LoggerFactory.getLogger(SuomiNPPFilter.class);
058        
059        private static final String PRODUCT_SEPARATOR = "-";
060        private static String PREV_DIRECTORY = null;
061        private static String DATA_DESCRIPTION = "JPSS Data";
062        private HashMap<String, File> seenGranules = new HashMap<String, File>();
063        private HashMap<String, File> validGranules = new HashMap<String, File>();
064        
065        public SuomiNPPFilter() {
066                super();
067        }
068        
069        // Accept all directories and all Suomi NPP files.
070        public boolean accept(File f) {
071                
072            if (f.isDirectory()) {
073                return true;
074            }
075            
076                // avoid constant rescans on window resizing and scrolling
077                String curDirectory = f.getParent();
078                if ((PREV_DIRECTORY != null) && (curDirectory.equals(PREV_DIRECTORY))) {
079                        if (seenGranules.containsKey(f.getName())) {
080                                // XXX TJJ - Still don't know why accept gets called so often...
081                                // Made a non-McV, standalone Swing app with JFileChooser and 
082                                // got same behavior - thousands of accept calls with mouse
083                                // movement, particularly two-finger scrolling on a Mac.
084                                if (validGranules.containsKey(f.getName())) {
085                                        return true;
086                                } else {
087                                        return false;
088                                }
089                        } else {
090                                seenGranules.put(f.getName(), f);
091                        }
092                } else {
093                        PREV_DIRECTORY = curDirectory;
094                        seenGranules.clear();
095                        validGranules.clear();
096                }
097
098            if (isSuomiNPPFile(f)) {
099                validGranules.put(f.getName(), f);
100                return true;
101            } else {
102                return false;
103            }
104
105        }
106        
107        /**
108         * Is this a Suomi NPP Product Data file?
109         * 
110         * @param f name of file to test
111         * @return      true if conditions met (mostly presence of geolocation)
112         */
113        
114        private boolean isSuomiNPPFile(File f) {
115                
116                boolean isSuomiNPP_NOAA = false;
117                boolean isSuomiNPP_NASA = false;
118                boolean isSuomiNPP = false;
119                
120                String fileNameRelative = f.getName();
121                String fileNameAbsolute = f.getParent() + File.separatorChar + f.getName();
122                
123                // null or empty filename
124                if ((fileNameRelative == null) || (fileNameRelative.equals(""))) return isSuomiNPP;
125                
126                // see if relative filename matches the Suomi NPP NOAA regular expression       
127                if (fileNameRelative.matches(JPSSUtilities.SUOMI_NPP_REGEX_NOAA)) {
128                        isSuomiNPP_NOAA = true;
129                        logger.debug(fileNameRelative + " matches Suomi NPP NOAA regex");
130                // else see if relative filename matches the Suomi NPP NASA regular expression
131                } else if (fileNameRelative.matches(JPSSUtilities.SUOMI_NPP_REGEX_NASA)) {
132                        isSuomiNPP_NASA = true;
133                        logger.debug(fileNameRelative + " matches Suomi NPP NASA regex");
134            // else see if relative filename matches the JPSS Enterprise EDR regex
135            } else if (fileNameRelative.matches(JPSSUtilities.JPSS_REGEX_ENTERPRISE_EDR)) {
136                logger.debug(fileNameRelative + " matches JPSS Enterprise EDR regex");
137                // no further checks needed for this flavor - geolocation is always bundled with data
138                return true;
139                } else {
140                        // don't go any further if file does not match Suomi NPP data product regex
141                logger.debug(fileNameRelative + " does not match any JPSS regex");
142                        return isSuomiNPP;
143                }
144                
145                // make sure a geolocation file is present if it does look like a valid Suomi NPP data file!
146                
147                // if a geo dataset is embedded in a multi-product file, we can call it good without
148                // having to open any files.  Just look for a geo product id in the filename.
149                // HOWEVER - if it's a single-product GEO-only file, disqualify that
150                if (isSuomiNPP_NOAA) {
151                        String prodStr = fileNameRelative.substring(0, fileNameRelative.indexOf(JPSSUtilities.JPSS_FIELD_SEPARATOR));
152                    StringTokenizer st = new StringTokenizer(prodStr, PRODUCT_SEPARATOR);
153                    int numTokens = st.countTokens();
154                    logger.trace("check for embedded GEO, tokenizing: " + prodStr);
155                    while (st.hasMoreTokens()) {
156                        String singleProd = st.nextToken();
157                        for (int i = 0; i < JPSSUtilities.geoProductIDs.length; i++) {
158                                if (singleProd.equals(JPSSUtilities.geoProductIDs[i])) {
159                                        logger.trace("Found embedded GEO: " + singleProd);
160                                        // if it's a single-product file, disqualify this as a GEO-only file!
161                                        if (numTokens == 1) {
162                                                return false;
163                                        } else {
164                                                if (numTokens > 1) {
165                                                        return true;
166                                                }
167                                        }
168                                }
169                        }
170                    }
171                }
172                
173                // looks like a standalone product - will have to look for separate geo file
174            // first, create the corresponding GEO loc file name
175                String geoProductID = null;
176                
177                boolean noGeo = false;
178                
179                if (isSuomiNPP_NOAA) {
180                        NetcdfFile ncfile = null;
181                        try {
182                                logger.debug("Trying to open file: " + fileNameAbsolute);
183                                ncfile = NetcdfFile.open(fileNameAbsolute);
184                                Attribute a = ncfile.findGlobalAttribute("N_GEO_Ref");
185                                // if no GEO attribute, we can't visualize this Suomi NPP data file, don't include it
186                                if (a == null) {
187                                        noGeo = true;
188                                } else {
189                                        // in the newest operational data format, attribute is entire file name
190                                        // if this is detected, no translation/mapping needed
191                                        String geoStr = a.getStringValue();
192                                        if (geoStr.matches(JPSSUtilities.SUOMI_GEO_REGEX_NOAA)) {
193                                                geoProductID = geoStr;
194                                        } else {
195                                                noGeo = true;
196                                        }
197                                }
198                        } catch (Exception e) {
199                                logger.error("Exception during open file: " + fileNameAbsolute, e);
200                        } finally {
201                                try {
202                                        if (ncfile != null) ncfile.close();
203                                } catch (IOException ioe) {
204                                        logger.error("Problem closing ncfile", ioe);
205                                }
206                        }
207                }
208                
209                if (isSuomiNPP_NASA) {
210                        geoProductID = fileNameRelative.replace("L1B", "GEO");
211                        logger.debug("Will be looking for NASA GEO file: " + geoProductID);
212                }
213                
214                // if no geolocation global attribute found, skip this file
215                if (noGeo) {
216                        isSuomiNPP = false;
217                } else {
218                
219                        if (isSuomiNPP_NOAA) {
220                                // ok, we know what the geo file is supposed to be, but is it present in this directory?
221                                String geoFilename = fileNameAbsolute.substring(0, fileNameAbsolute.lastIndexOf(File.separatorChar) + 1);
222                                geoFilename += geoProductID;
223                                
224                                // first check for the typically referenced ellipsoid geolocation
225                                if (! isSuomiNPP) {
226                                        geoFilename = geoFilename.substring(geoFilename.lastIndexOf(File.separatorChar) + 1);
227                                        
228                                        // now we make a file filter, and see if a matching geo file is present
229                                        File fList = new File(fileNameAbsolute.substring(0, fileNameAbsolute.lastIndexOf(File.separatorChar) + 1)); // current directory
230        
231                                        FilenameFilter geoFilter = new FilenameFilter() {
232                                                public boolean accept(File dir, String name) {
233                                                        if (name.matches(JPSSUtilities.SUOMI_GEO_REGEX_NOAA)) {
234                                                                return true;
235                                                        } else {
236                                                                return false;
237                                                        }
238                                                }
239                                        };
240                                        
241                                        File[] files = fList.listFiles(geoFilter);
242                                        for (File file : files) {
243                                                if (file.isDirectory()) {
244                                                        continue;
245                                                }
246                                                // get the file name for convenience
247                                                String fName = file.getName();
248                                                // is it one of the geo types we are looking for?
249                                                if (fName.substring(0, 5).equals(geoFilename.substring(0, 5))) {
250                                                        int geoStartIdx = geoFilename.indexOf("_d");
251                                                        int prdStartIdx = fileNameRelative.indexOf("_d");
252                                                        String s1 = 
253                                                                geoFilename.substring(geoStartIdx, geoStartIdx + JPSSUtilities.NOAA_CREATION_DATE_INDEX);
254                                                        String s2 = 
255                                                                fileNameRelative.substring(prdStartIdx, prdStartIdx + JPSSUtilities.NOAA_CREATION_DATE_INDEX);
256                                                        if (s1.equals(s2)) {
257                                                                isSuomiNPP = true;
258                                                                break;
259                                                        }
260                                                }
261                                        }
262        
263                                } 
264
265                                // one last thing to check, if no luck so far...
266                                // are we using terrain-corrected geolocation?
267                                if (! isSuomiNPP) {
268                                        geoFilename = geoFilename.substring(geoFilename.lastIndexOf(File.separatorChar) + 1);
269                                        // this one looks for GMTCO instead of GMODO
270                                        geoFilename = geoFilename.replace("OD", "TC");
271                                        // this one looks for GITCO instead of GIMGO
272                                        geoFilename = geoFilename.replace("MG", "TC");
273                                        
274                                        // now we make a file filter, and see if a matching geo file is present
275                                        File fList = new File(fileNameAbsolute.substring(0, fileNameAbsolute.lastIndexOf(File.separatorChar) + 1)); // current directory
276        
277                                        FilenameFilter geoFilter = new FilenameFilter() {
278                                                public boolean accept(File dir, String name) {
279                                                        if (name.matches(JPSSUtilities.SUOMI_GEO_REGEX_NOAA)) {
280                                                                return true;
281                                                        } else {
282                                                                return false;
283                                                        }
284                                                }
285                                        };
286                                        
287                                        File[] files = fList.listFiles(geoFilter);
288                                        for (File file : files) {
289                                                if (file.isDirectory()) {
290                                                        continue;
291                                                }
292                                                // get the file name for convenience
293                                                String fName = file.getName();
294                                                // is it one of the geo types we are looking for?
295                                                if (fName.substring(0, 5).equals(geoFilename.substring(0, 5))) {
296                                                        int geoStartIdx = geoFilename.indexOf("_d");
297                                                        int prdStartIdx = fileNameRelative.indexOf("_d");
298                                                        String s1 = 
299                                                                geoFilename.substring(geoStartIdx, geoStartIdx + JPSSUtilities.NOAA_CREATION_DATE_INDEX);
300                                                        String s2 = 
301                                                                fileNameRelative.substring(prdStartIdx, prdStartIdx + JPSSUtilities.NOAA_CREATION_DATE_INDEX);
302                                                        if (s1.equals(s2)) {
303                                                                isSuomiNPP = true;
304                                                                break;
305                                                        }
306                                                }
307                                        }
308                                }
309                        }
310                        
311                        if (isSuomiNPP_NASA) {
312                                logger.debug("Checking for NASA-style GEO...");
313                                String geoFilename = geoProductID;
314                                logger.debug("Target FileName: " + geoFilename);
315                                
316                                // now we make a file filter, and see if a matching geo file is present
317                                File fList = new File(
318                                                fileNameAbsolute.substring(0, fileNameAbsolute.lastIndexOf(File.separatorChar) + 1)
319                                );
320
321                                FilenameFilter geoFilter = new FilenameFilter() {
322                                        public boolean accept(File dir, String name) {
323                                                if (name.matches(JPSSUtilities.SUOMI_GEO_REGEX_NASA)) {
324                                                        return true;
325                                                } else {
326                                                        return false;
327                                                }
328                                        }
329                                };
330                                
331                                File[] files = fList.listFiles(geoFilter);
332                                for (File file : files) {
333                                        if (file.isDirectory()) {
334                                                continue;
335                                        }
336                                        // get the file name for convenience
337                                        String fName = file.getName();
338                                        logger.debug("FileName: " + fName);
339                                        // is it one of the geo types we are looking for?
340                                        if (fName.substring(0, 5).equals(geoFilename.substring(0, 5))) {
341                                                int geoStartIdx = geoFilename.indexOf("_d");
342                                                int prdStartIdx = fileNameRelative.indexOf("_d");
343                                                String s1 = 
344                                                        geoFilename.substring(geoStartIdx, geoStartIdx + JPSSUtilities.NASA_CREATION_DATE_INDEX);
345                                                String s2 = 
346                                                        fileNameRelative.substring(prdStartIdx, prdStartIdx + JPSSUtilities.NASA_CREATION_DATE_INDEX);
347                                                if (s1.equals(s2)) {
348                                                        isSuomiNPP = true;
349                                                        break;
350                                                }
351                                        }
352                                }
353                        }
354                }
355                
356                return isSuomiNPP;
357        }
358        
359        // The description of this filter
360        public String getDescription() {
361            return DATA_DESCRIPTION;
362        }
363        
364    }