001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2015 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.chooser; 030 031import java.awt.BorderLayout; 032import java.io.File; 033 034import java.text.ParseException; 035import java.text.SimpleDateFormat; 036import java.util.ArrayList; 037import java.util.Date; 038import java.util.Vector; 039 040import javax.swing.JFileChooser; 041import javax.swing.JOptionPane; 042import javax.swing.JPanel; 043import javax.swing.filechooser.FileFilter; 044 045import org.slf4j.Logger; 046import org.slf4j.LoggerFactory; 047 048import edu.wisc.ssec.mcidasv.data.hydra.JPSSUtilities; 049 050import ucar.unidata.idv.chooser.IdvChooserManager; 051import ucar.unidata.util.StringUtil; 052 053public class SuomiNPPChooser extends FileChooser { 054 055 private static final long serialVersionUID = 1L; 056 private static final Logger logger = LoggerFactory.getLogger(SuomiNPPChooser.class); 057 private static final long CONSECUTIVE_GRANULE_MAX_GAP_MS = 60000; 058 059 // date formatter for converting Suomi NPP day/time from file name for consecutive granule check 060 private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmSSS"); 061 062 /** 063 * Create the chooser with the given manager and xml 064 * 065 * @param mgr The manager 066 * @param root The xml 067 * 068 */ 069 070 public SuomiNPPChooser(IdvChooserManager mgr, org.w3c.dom.Element root) { 071 super(mgr, root); 072 } 073 074 /** 075 * Make the file chooser 076 * 077 * @param path the initial path 078 * 079 * @return the file chooser 080 */ 081 082 protected JFileChooser doMakeFileChooser(String path) { 083 if (fileChooser == null) { 084 logger.debug("Creating Suomi NPP File Chooser..."); 085 fileChooser = new SuomiNPPFileChooser(path, this); 086 } else { 087 logger.debug("2nd call to doMakeFileChooser, why?"); 088 } 089 return fileChooser; 090 } 091 092 /** 093 * Handle the selection of the set of files 094 * 095 * @param files The files the user chose 096 * @param directory The directory they chose them from 097 * @return True if the file was successful 098 * @throws Exception 099 */ 100 101 protected boolean selectFilesInner(File[] files, File directory) 102 throws Exception { 103 if ((files == null) || (files.length == 0)) { 104 userMessage("Please select a file"); 105 return false; 106 } 107 108 // make a list of just the file names 109 ArrayList<String> fileNames = new ArrayList<String>(); 110 for (int i = 0; i < files.length; i++) { 111 fileNames.add(files[i].getName()); 112 } 113 114 // ensure these files make sense as a set to create a single SNPP data source 115 if (! JPSSUtilities.isValidSet(fileNames)) { 116 JOptionPane.showMessageDialog(null, 117 "Unable to group selected data as a single data source."); 118 return false; 119 } 120 121 // ensure these files make sense as a set to create a single SNPP data source 122 if (! JPSSUtilities.hasCommonGeo(fileNames, directory)) { 123 JOptionPane.showMessageDialog(null, 124 "Unable to group selected data as a single data source."); 125 return false; 126 } 127 128 // At present, Suomi NPP chooser only allows selecting sets of consecutive granules 129 boolean granulesAreConsecutive = testConsecutiveGranules(files); 130 if (granulesAreConsecutive) { 131 return super.selectFilesInner(files, directory); 132 } else { 133 // throw up a dialog to tell user the problem 134 JOptionPane.showMessageDialog(this, "When selecting multiple granules, they must be consecutive."); 135 } 136 return false; 137 } 138 139 /** 140 * Test whether a set of files are consecutive Suomi NPP granules, 141 * any sensor. NOTE: This method works when the file list contains 142 * multiple products ONLY because once we've validate one product, 143 * the time check will be a negative number when comparing the FIRST 144 * granule of product 2 with the LAST granule of product 1. A better 145 * implementation would be to pass in the filename map like the 146 * one generated in SuomiNPPDataSource constructor. 147 * 148 * @param files 149 * @return true if consecutive tests pass for all files 150 */ 151 152 private boolean testConsecutiveGranules(File[] files) { 153 boolean testResult = false; 154 if (files == null) return testResult; 155 // compare start time of current granule with end time of previous 156 // difference should be very small - under a second 157 long prvTime = -1; 158 testResult = true; 159 for (int i = 0; i < files.length; i++) { 160 if ((files[i] != null) && !files[i].isDirectory()) { 161 if (files[i].exists()) { 162 String fileName = files[i].getName(); 163 int dateIndex = fileName.lastIndexOf("_d2") + 2; 164 int timeIndexStart = fileName.lastIndexOf("_t") + 2; 165 int timeIndexEnd = fileName.lastIndexOf("_e") + 2; 166 String dateStr = fileName.substring(dateIndex, dateIndex + 8); 167 String timeStrStart = fileName.substring(timeIndexStart, timeIndexStart + 7); 168 String timeStrEnd = fileName.substring(timeIndexEnd, timeIndexEnd + 7); 169 // sanity check on file name lengths 170 int fnLen = fileName.length(); 171 if ((dateIndex > fnLen) || (timeIndexStart > fnLen) || (timeIndexEnd > fnLen)) { 172 logger.warn("unexpected file name length for: " + fileName); 173 testResult = false; 174 break; 175 } 176 // pull start and end time out of file name 177 Date dS = null; 178 Date dE = null; 179 try { 180 dS = sdf.parse(dateStr + timeStrStart); 181 // due to nature of Suomi NPP file name encoding, we need a special 182 // check here - end time CAN roll over to next day, while day part 183 // does not change. if this happens, we tweak the date string 184 String endDateStr = dateStr; 185 String startHour = timeStrStart.substring(0, 2); 186 String endHour = timeStrEnd.substring(0, 2); 187 if ((startHour.equals("23")) && (endHour.equals("00"))) { 188 // temporarily convert date to integer, increment, convert back 189 int tmpDate = Integer.parseInt(dateStr); 190 tmpDate++; 191 endDateStr = "" + tmpDate; 192 logger.info("Granule time spanning days case handled ok..."); 193 } 194 dE = sdf.parse(endDateStr + timeStrEnd); 195 } catch (ParseException e) { 196 logger.error("Not recognized as valid Suomi NPP file name: " + fileName); 197 testResult = false; 198 break; 199 } 200 long curTime = dS.getTime(); 201 long endTime = dE.getTime(); 202 // only check current with previous 203 if (prvTime > 0) { 204 // make sure time diff does not exceed allowed threshold 205 // consecutive granules should be less than 1 minute apart 206 if ((curTime - prvTime) > CONSECUTIVE_GRANULE_MAX_GAP_MS) { 207 testResult = false; 208 break; 209 } 210 } 211 prvTime = endTime; 212 } 213 } 214 } 215 return testResult; 216 } 217 218 /** 219 * Convert the given array of File objects 220 * to an array of String file names. Only 221 * include the files that actually exist. 222 * 223 * @param files Selected files 224 * @return Selected files as Strings 225 */ 226 227 protected String[] getFileNames(File[] files) { 228 if (files == null) { 229 return (String[]) null; 230 } 231 Vector<String> v = new Vector<String>(); 232 String fileNotExistsError = ""; 233 234 // NOTE: If multiple files are selected, then missing files 235 // are not in the files array. If one file is selected and 236 // it is not there, then it is in the array and file.exists() 237 // is false 238 for (int i = 0; i < files.length; i++) { 239 if ((files[i] != null) && !files[i].isDirectory()) { 240 if ( !files[i].exists()) { 241 fileNotExistsError += "File does not exist: " + files[i] + "\n"; 242 } else { 243 v.add(files[i].toString()); 244 } 245 } 246 } 247 248 if (fileNotExistsError.length() > 0) { 249 userMessage(fileNotExistsError); 250 return null; 251 } 252 253 return v.isEmpty() 254 ? null 255 : StringUtil.listToStringArray(v); 256 } 257 258 /** 259 * Get the bottom panel for the chooser 260 * @return the bottom panel 261 */ 262 263 protected JPanel getBottomPanel() { 264 // No bottom panel at present 265 return null; 266 } 267 268 /** 269 * Get the center panel for the chooser 270 * @return the center panel 271 */ 272 273 protected JPanel getCenterPanel() { 274 JPanel centerPanel = super.getCenterPanel(); 275 276 JPanel jp = new JPanel(new BorderLayout()) { 277 public void paint(java.awt.Graphics g) { 278 FileFilter ff = fileChooser.getFileFilter(); 279 if (! (ff instanceof SuomiNPPFilter)) { 280 fileChooser.setAcceptAllFileFilterUsed(false); 281 fileChooser.setFileFilter(new SuomiNPPFilter()); 282 } 283 super.paint(g); 284 } 285 }; 286 jp.add(centerPanel); 287 288 return jp; 289 } 290 291}