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.data.hydra;
030
031import java.io.File;
032import java.io.IOException;
033import java.util.ArrayList;
034import java.util.HashMap;
035import java.util.HashSet;
036import java.util.Iterator;
037import java.util.List;
038import java.util.Map;
039import java.util.Set;
040import java.util.StringTokenizer;
041
042import ucar.nc2.Attribute;
043import ucar.nc2.NetcdfFile;
044
045/**
046 * Utility class to support Joint Polar Satellite System (JPSS) functionality.
047 * Documentation referenced is from Suomi NPP Common Data Format Control Book.
048 * See:
049 * http://jointmission.gsfc.nasa.gov/science/documents.html
050 * 
051 * @author tommyj
052 *
053 */
054
055public abstract class JPSSUtilities {
056   
057        public static final String JPSS_FIELD_SEPARATOR = "_";
058        
059        // This regular expression matches a Suomi NPP Data Product as defined by the 
060        // spec in CDFCB-X Volume 1, Page 21
061        public static final String SUOMI_NPP_REGEX =
062                // Product Id, Multiple (ex: VSSTO-GATMO-VSLTO)
063                "(\\w\\w\\w\\w\\w-)*" + 
064                // Product Id, Single (ex: VSSTO)
065                "\\w\\w\\w\\w\\w" + JPSSUtilities.JPSS_FIELD_SEPARATOR +
066                // Spacecraft Id (ex: npp)
067                "\\w\\w\\w" + JPSSUtilities.JPSS_FIELD_SEPARATOR +
068                // Data Start Date (ex: dYYYYMMDD)
069                "d20[0-3]\\d[0-1]\\d[0-3]\\d" + JPSSUtilities.JPSS_FIELD_SEPARATOR +
070                // Data Start Time (ex: tHHMMSSS)
071                "t[0-2]\\d[0-5]\\d[0-6]\\d\\d" + JPSSUtilities.JPSS_FIELD_SEPARATOR +
072                // Data Stop Time (ex: eHHMMSSS)
073                "e[0-2]\\d[0-5]\\d[0-6]\\d\\d" + JPSSUtilities.JPSS_FIELD_SEPARATOR +
074                // Orbit Number (ex: b00015)
075                "b\\d\\d\\d\\d\\d" + JPSSUtilities.JPSS_FIELD_SEPARATOR +
076                // Creation Date (ex: cYYYYMMDDHHMMSSSSSSSS)
077                "c20[0-3]\\d[0-1]\\d[0-3]\\d[0-2]\\d[0-5]\\d[0-6]\\d\\d\\d\\d\\d\\d\\d" + JPSSUtilities.JPSS_FIELD_SEPARATOR +
078                // Origin (ex: navo)
079                "\\w\\w\\w\\w" + JPSSUtilities.JPSS_FIELD_SEPARATOR +
080                // Domain (ex: ops)
081                "\\w\\w\\w" + 
082                // HDF5 suffix
083                ".h5";
084        
085        public static float[] ATMSChannelCenterFrequencies = {
086                23.8f,
087                31.4f,
088                50.3f,
089                51.76f,
090                52.8f,
091                53.596f,
092                54.40f,
093                54.94f,
094                55.50f,
095                57.29032f,
096                57.29033f,
097                57.29034f,
098                57.29035f,
099                57.29036f,
100                57.29037f,
101                88.20f,
102                165.5f,
103                183.3101f,
104                183.3102f,
105                183.3103f,
106                183.3104f,
107                183.3105f
108        };
109        
110        // the list of valid geolocation product ids
111        public static String[] geoProductIDs = {
112        "GATMO",
113        "GCRSO",
114        "GAERO",
115        "GCLDO",
116        "GDNBO",
117        "GNCCO",
118        "GIGTO",
119        "GIMGO",
120        "GITCO",
121        "GMGTO",
122        "GMODO",
123        "GMTCO",
124        "GNHFO",
125        "GOTCO",
126        "GOSCO",
127        "GONPO",
128        "GONCO",
129        "GCRIO",
130        "GATRO",
131        "IVMIM",
132        "VMUGE"
133        };  
134        
135        // This regular expression matches a Suomi NPP geolocation granule, see 
136        // spec in CDFCB-X Volume 1, Page 21
137        public static final String SUOMI_GEO_REGEX =
138                // Geo Id, Single (ex: GMODO)
139                        // NOTE: This MUST match the list of product ids in static array above!
140                "(GATMO|GCRSO|GAERO|GCLDO|GDNBO|GNCCO|GIGTO|GIMGO|GITCO|" + 
141                        "GMGTO|GMODO|GMTCO|GNHFO|GOTCO|GOSCO|GONPO|GONCO|GCRIO|GATRO|IVMIM|VMUGE)" + 
142                        JPSSUtilities.JPSS_FIELD_SEPARATOR +
143                // Spacecraft Id (ex: npp)
144                "\\w\\w\\w" + JPSSUtilities.JPSS_FIELD_SEPARATOR +
145                // Data Start Date (ex: dYYYYMMDD)
146                "d20[0-3]\\d[0-1]\\d[0-3]\\d" + JPSSUtilities.JPSS_FIELD_SEPARATOR +
147                // Data Start Time (ex: tHHMMSSS)
148                "t[0-2]\\d[0-5]\\d[0-6]\\d\\d" + JPSSUtilities.JPSS_FIELD_SEPARATOR +
149                // Data Stop Time (ex: eHHMMSSS)
150                "e[0-2]\\d[0-5]\\d[0-6]\\d\\d" + JPSSUtilities.JPSS_FIELD_SEPARATOR +
151                // Orbit Number (ex: b00015)
152                "b\\d\\d\\d\\d\\d" + JPSSUtilities.JPSS_FIELD_SEPARATOR +
153                // Creation Date (ex: cYYYYMMDDHHMMSSSSSSSS)
154                "c20[0-3]\\d[0-1]\\d[0-3]\\d[0-2]\\d[0-5]\\d[0-6]\\d\\d\\d\\d\\d\\d\\d" + JPSSUtilities.JPSS_FIELD_SEPARATOR +
155                // Origin (ex: navo)
156                "\\w\\w\\w\\w" + JPSSUtilities.JPSS_FIELD_SEPARATOR +
157                // Domain (ex: ops)
158                "\\w\\w\\w" + 
159                // HDF5 suffix
160                ".h5";
161        
162        /**
163         * Determine if the set if filenames constitutes contiguous SNPP granules of the
164         * same geographic coverage.
165         * 
166         * @return True if passes checks
167         */
168        
169        public static boolean isValidSet(List fileList) {
170                
171                // map with filename from start date through orbit will be used for comparisons
172        Map metadataMap = new HashMap<String, List<String>>();
173        
174        // Pass 1, populate the list of products selected, and empty maps
175        for (Object o : fileList) {
176                String filename = (String) o;
177                // start at last path separator to clip off absolute paths
178                int lastSeparator = filename.lastIndexOf(File.separatorChar);
179                // products go to first underscore, see regex above for more detail
180                int firstUnderscore = filename.indexOf("_", lastSeparator + 1);
181                String prodStr = filename.substring(lastSeparator + 1, firstUnderscore);
182                if (! metadataMap.containsKey(prodStr)) {
183                                List<String> l = new ArrayList<String>();
184                                metadataMap.put(prodStr, l);
185                }
186        }
187        
188        // Pass 2, build up the lists of meta data strings and full filenames
189        for (Object o : fileList) {
190                String filename = (String) o;
191                // start at last path separator to clip off absolute paths
192                int lastSeparator = filename.lastIndexOf(File.separatorChar);
193                // products go to first underscore, see regex above for more detail
194                int firstUnderscore = filename.indexOf("_", lastSeparator + 1);
195                // this is the key for the maps
196                String prodStr = filename.substring(lastSeparator + 1, firstUnderscore);
197                // this is the value for the meta data map - start time through orbit 
198                String metaStr = filename.substring(firstUnderscore + 1, firstUnderscore + 39);
199                // get the appropriate list, add the new value
200                List l = (List) metadataMap.get(prodStr);
201                l.add(metaStr);
202                metadataMap.put(prodStr, l);
203        }
204        
205        // loop over metadata map, every list much match the one for ALL other products
206        Set s = metadataMap.keySet();
207        Iterator iterator = s.iterator();
208        List prvList = null;
209        while (iterator.hasNext()) {
210                String key = (String) iterator.next();
211                List l = (List) metadataMap.get(key);
212                for (int i = 0; i < l.size(); i++) {
213                        if (prvList != null) {
214                                if (! l.equals(prvList)) return false;
215                        }
216                }
217                prvList = l;
218        }
219        
220                return true;
221        }
222
223        /**
224         * Determine if a set if filenames which constitutes contiguous SNPP granules of 
225         * various products all share the same geolocation data type.
226         * 
227         * @return True if passes checks
228         */
229        
230        public static boolean hasCommonGeo(List fileList, File directory) {
231                Set<String> s = new HashSet<String>();
232                boolean isCombinedProduct = false;
233                
234                // loop through all filenames provided
235        for (Object o : fileList) {
236                isCombinedProduct = false;
237                String filename = (String) o;
238                
239                // check the case where GEO is embedded in the data granules
240                int lastSeparator = filename.lastIndexOf(File.separatorChar);
241                int firstUnderscore = filename.indexOf("_", lastSeparator + 1);
242                String prodStr = filename.substring(lastSeparator + 1, firstUnderscore);
243            StringTokenizer st = new StringTokenizer(prodStr, "-");
244            while (st.hasMoreTokens()) {
245                String singleProd = st.nextToken();
246                for (int i = 0; i < JPSSUtilities.geoProductIDs.length; i++) {
247                        if (singleProd.equals(JPSSUtilities.geoProductIDs[i])) {
248                                s.add(singleProd);
249                                isCombinedProduct = true;
250                                break;
251                        }
252                }
253            }
254            // GEO not embedded in file, need to see which GEO file is referenced in 
255            // the global attribute
256            if (! isCombinedProduct) {
257                try {
258                        String fileFullPath = directory.getAbsolutePath() + File.separator + filename;
259                                        NetcdfFile ncfile = NetcdfFile.open(fileFullPath);
260                                        Attribute a = ncfile.findGlobalAttribute("N_GEO_Ref");
261                                        if (a != null) {
262                                                String geoFromAttr = a.getStringValue().substring(0, 5);
263                                                s.add(geoFromAttr);
264                                        }
265                                        ncfile.close();
266                                } catch (IOException ioe) {
267                                        ioe.printStackTrace();
268                                }
269            }
270        }
271        
272        // if the products chosen utilize multiple GEO types, fail the selection
273        if (s.size() > 1) return false;
274                return true;
275        }
276        
277}