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 }