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.data.hrit;
030
031import java.awt.BorderLayout;
032import java.awt.FlowLayout;
033import java.io.File;
034import java.io.IOException;
035import java.rmi.RemoteException;
036import java.util.ArrayList;
037import java.util.Hashtable;
038import java.util.List;
039
040import javax.swing.JComboBox;
041import javax.swing.JComponent;
042import javax.swing.JPanel;
043
044import edu.wisc.ssec.mcidas.Calibrator;
045
046import org.slf4j.Logger;
047import org.slf4j.LoggerFactory;
048import visad.Data;
049import visad.VisADException;
050import visad.data.hrit.HRITAdapter;
051
052import ucar.unidata.data.DataCategory;
053import ucar.unidata.data.DataChoice;
054import ucar.unidata.data.DataSelection;
055import ucar.unidata.data.DataSelectionComponent;
056import ucar.unidata.data.DataSourceDescriptor;
057import ucar.unidata.data.DataSourceImpl;
058import ucar.unidata.data.DirectDataChoice;
059import ucar.unidata.util.Misc;
060import ucar.unidata.util.WrapperException;
061
062public class HRITDataSource extends DataSourceImpl  {
063    
064    private static final Logger logger =
065        LoggerFactory.getLogger(HRITDataSource.class);
066    
067    /** List of sources files */
068    protected List sources;
069    
070    public static String request;
071    
072    /** List of sources files */
073    protected List adapters;
074    
075    private List categories;
076    
077    /** for unpersistence */
078    protected String oldSourceFromBundles;
079    
080    private static final String DATA_DESCRIPTION = "HRIT Data";
081    
082    private static int counter = 1;
083    
084    /** children choices */
085    private List myDataChoices = new ArrayList();
086    
087    /**
088     * Default constructor
089     */
090    public HRITDataSource() {}
091    
092    /**
093     * Construct a new HRIT data source.
094     * @param  descriptor  descriptor for this {@code DataSource}
095     * @param  fileName  name of the HRIT segment file to read
096     * @param  properties  hashtable of properties
097     *
098     * @throws VisADException problem creating data
099     */
100    public HRITDataSource(DataSourceDescriptor descriptor,
101                          String fileName, Hashtable properties)
102        throws VisADException {
103        this(descriptor, Misc.newList(fileName), properties);
104    }
105    
106    /**
107     * Construct a new HRIT data source.
108     * @param  descriptor  descriptor for this {@code DataSource}
109     * @param  newSources  List of filenames
110     * @param  properties  hashtable of properties
111     *
112     * @throws VisADException problem creating data
113     */
114    public HRITDataSource(DataSourceDescriptor descriptor,
115                          List newSources, Hashtable properties)
116        throws VisADException {
117        
118        this(descriptor, newSources, DATA_DESCRIPTION, properties);
119        boolean looksOk = false;
120        String dataCategoryStr = "HRIT Data";
121        if ((newSources != null) && (newSources.size() >= 1)) {
122            String fileNameFullPath = (String) newSources.get(0);
123            if ((fileNameFullPath != null) && (fileNameFullPath.length() >= 58)) {
124                if ((fileNameFullPath.contains("MSG2")) && (fileNameFullPath.endsWith("-__"))) {
125                    String channelStr = fileNameFullPath.substring(fileNameFullPath.lastIndexOf("MSG2") + 13, fileNameFullPath.lastIndexOf("MSG2") + 19);
126                    String timeStr = fileNameFullPath.substring(fileNameFullPath.lastIndexOf("MSG2") + 33, fileNameFullPath.lastIndexOf("MSG2") + 45);
127                    dataCategoryStr = "MSG2 " + channelStr + " " + timeStr;
128                    looksOk = true;
129                }
130            }
131        }
132        if (looksOk) {
133            DataCategory.createCategory(dataCategoryStr);
134            categories = DataCategory.parseCategories(dataCategoryStr + ";IMAGE");
135        } else {
136            throw new VisADException("Not a decompressed MSG HRIT file");
137        }
138    }
139    
140    /**
141     * Create a HRITDataSource
142     *
143     * @param descriptor The datasource descriptor
144     * @param newSources List of files or urls
145     * @param description The long name
146     * @param properties properties
147     *
148     * @throws VisADException  couldn't create the data
149     */
150    public HRITDataSource(DataSourceDescriptor descriptor, List newSources,
151                          String description, Hashtable properties)
152        throws VisADException {
153        
154        super(descriptor, "HRIT" + counter, "HRIT" + counter, properties);
155        counter++;
156        sources = newSources;
157    }
158    
159    
160    /**
161     * Can this data source save its data to local disk
162     *
163     * @return can save to local disk
164     */
165    public boolean canSaveDataToLocalDisk() {
166        return !isFileBased() && (getProperty(PROP_SERVICE_HTTP) != null);
167    }
168    
169    
170    /**
171     * Are we getting data from a file or from server
172     *
173     * @return is the data from files
174     */
175    protected boolean isFileBased() {
176        if (sources.size() == 0) {
177            return false;
178        }
179        return (new File(sources.get(0).toString())).exists();
180    }
181    
182    /**
183     * This is called when the CacheManager detects the need ot clear memory.
184     * It is intended to be overwritten by derived classes that are holding cached
185     * data that is not in the normal putCache facilities provided by this class
186     * since that data is actually managed by the CacheManager
187     */
188    public void clearCachedData() {
189        super.clearCachedData();
190    }
191    
192    /**
193     * Make and insert the {@link DataChoice DataChoices} for this {@code DataSource}.
194     */
195    public void doMakeDataChoices() {
196        DataChoice choice = null;
197        
198        for (int i = 0; i < sources.size(); i++) {
199            String fileNameFullPath = (String) sources.get(i);
200            if (fileNameFullPath.contains("MSG2")) {
201                String channelStr = fileNameFullPath.substring(fileNameFullPath.lastIndexOf("MSG2") + 13, fileNameFullPath.lastIndexOf("MSG2") + 19);
202                String timeStr = fileNameFullPath.substring(fileNameFullPath.lastIndexOf("MSG2") + 33, fileNameFullPath.lastIndexOf("MSG2") + 45);
203                String segStr = fileNameFullPath.substring(fileNameFullPath.lastIndexOf("MSG2") + 27, fileNameFullPath.lastIndexOf("MSG2") + 29);
204                try {
205                    choice = doMakeDataChoice(0, "MSG2 " + channelStr + " " + timeStr + " SEGMENT " + segStr);
206                } catch (Exception e) {
207                    logger.error("Problem creating dataChoice", e);
208                }
209                
210                if (choice != null) {
211                    addDataChoice(choice);
212                }
213            }
214        }
215        
216    }
217    
218    private DataChoice doMakeDataChoice(int idx, String var) throws Exception {
219        String name = var;
220        Hashtable ht = null;
221        DirectDataChoice ddc = new DirectDataChoice(this, idx, name, name, categories, ht);
222        return ddc;
223    }
224    
225    /**
226     * Create, if needed, and return the list of adapters.
227     * Will return null if there are no valid adapters.
228     *
229     * @return List of adapters or null
230     */
231    protected List getAdapters() {
232        if ((adapters == null) || (adapters.size() == 0)) {
233            try {
234                makeAdapters(sources);
235            } catch (Exception exc) {
236                setInError(true);
237                throw new WrapperException(exc);
238            }
239        }
240        if (adapters.size() == 0) {
241            adapters = null;
242        }
243        return adapters;
244    }
245    
246    /**
247     * Make the adapters for the given list of files
248     *
249     * @param files Data files
250     *
251     * @throws Exception When bad things happen
252     */
253    private void makeAdapters(List files) throws Exception {
254        adapters = new ArrayList();
255    }
256    
257    
258    /**
259     * Create the list of times associated with this DataSource.
260     * @return list of times.
261     */
262    protected List doMakeDateTimes() {
263        List    times      = new ArrayList();
264        return times;
265    }
266    
267    /**
268     * Get the data for the given DataChoice and selection criteria.
269     * @param dataChoice         DataChoice for selection
270     * @param category           DataCategory for the DataChoice (not used)
271     * @param dataparams         Resolution criteria.
272     * @param requestProperties  extra request properties
273     * @return  the Data object for the request
274     *
275     * @throws RemoteException couldn't create a remote data object
276     * @throws VisADException  couldn't create the data
277     */
278    protected Data getDataInner(DataChoice dataChoice, DataCategory category,
279                                DataSelection dataparams,
280                                Hashtable requestProperties)
281        throws VisADException, RemoteException {
282        
283        // for now, hardcoded array of band center wave numbers, such that'
284        // the array index is the band number
285        String[] bandCWN = {
286            "N/A", "006", "008", "016", "039", "062", "073",
287            "087", "097", "108", "120", "134", "___"
288        };
289        
290        // XXX TJJ need to determine this from data type and wavelength
291        int bandNum = 1;
292        // default to BRIT calibration, will check if user picked something else
293        int calType = Calibrator.CAL_BRIT;
294        
295        String newRes = (String) dataparams.getProperty("magnification");
296        int magFactor = 1;
297        if (newRes != null) {
298            try {
299                magFactor = Integer.parseInt(newRes);
300            } catch (NumberFormatException nfe) {
301                String msg = String.format("Could not parse '%s'", newRes);
302                logger.error(msg, nfe);
303            }
304        }
305        
306        // pull out source index 
307        String idxStr = dataChoice.getName().substring(dataChoice.getName().length() - 2, dataChoice.getName().length());
308        
309        Data data = null;
310        
311        String [] files = new String[1];
312        // initialize central wave number string
313        String cwnStr = "006";
314        for (int i = 0; i < sources.size(); i++) {
315            String tmpStr = (String) sources.get(i);
316            cwnStr = tmpStr.substring(tmpStr.lastIndexOf("MSG2") + 16, tmpStr.lastIndexOf("MSG2") + 19);
317            String segStr = tmpStr.substring(tmpStr.lastIndexOf("MSG2") + 27, tmpStr.lastIndexOf("MSG2") + 29);
318            if (segStr.equals(idxStr)) {
319                files[0] = (String) sources.get(i);
320            }
321        }
322        
323        // match up central wave number with band number index
324        for (int i = 0; i < bandCWN.length; i++) {
325            if (bandCWN[i].equals(cwnStr)) {
326                bandNum = i;
327                break;
328            }
329        }
330        
331        String newCal = (String) dataparams.getProperty("calibration");
332        // do checks to only allow valid calibrations here
333        if (newCal != null) {
334            if ((bandNum >= 4) && (bandNum <= 11)) {
335                if (newCal.equals("RAD")) {
336                    calType = Calibrator.CAL_RAD;
337                }
338                if (newCal.equals("TEMP")) {
339                    calType = Calibrator.CAL_TEMP;
340                }
341                if (newCal.equals("BRIT")) {
342                    calType = Calibrator.CAL_BRIT;
343                }
344            } else {
345                if (newCal.equals("RAD")) {
346                    calType = Calibrator.CAL_RAD;
347                }
348                if (newCal.equals("ALB")) {
349                    calType = Calibrator.CAL_ALB;
350                }
351                if (newCal.equals("BRIT")) {
352                    calType = Calibrator.CAL_BRIT;
353                }
354            }
355        }
356        
357        HRITAdapter ha;
358        try {
359            ha = new HRITAdapter(files, magFactor, calType, bandNum);
360            data = ha.getData();
361        } catch (IOException e) {
362            logger.error("Problem getting data", e);
363        }
364        
365        return data;
366    }
367    
368    protected void initDataSelectionComponents(
369        List<DataSelectionComponent> components,
370        final DataChoice dataChoice) {
371        
372        try {
373            components.add(new ResolutionSelection(dataChoice));
374        } catch (Exception e) {
375            logger.error("Problem creating new ResolutionSelection", e);
376        }
377    }
378    
379    
380    class ResolutionSelection extends DataSelectionComponent {
381        
382        DataChoice dataChoice;
383        JPanel display;
384        JComboBox jcbMag = null;
385        JComboBox jcbCal = null;
386        
387        ResolutionSelection(DataChoice dataChoice) throws Exception {
388            super("Magnification and Calibration");
389            this.dataChoice = dataChoice;
390            List names = dataChoice.getCurrentNames();
391            display = new JPanel(new FlowLayout());
392            String[] resStrings = { "1", "2", "4", "8", "16" };
393            jcbMag = new JComboBox(resStrings);
394            display.add(jcbMag);
395            String[] irCalStrings  = { "BRIT", "RAD", "RAW", "TEMP" };
396            String[] visCalStrings = { "BRIT", "RAD", "RAW", "ALB" };
397            // XXX TJJ - we need a standard mechanism to make this determination
398            // this is a temporary cheap hack: grab the last file name added and
399            // do a hardcoded string match.
400            String sampleFileName = names.get(names.size() - 1).toString();
401            // those below are considered "visible" bands, yes even IR_016!
402            if ((sampleFileName.contains("VIS")) ||
403                (sampleFileName.contains("HRV")) ||
404                (sampleFileName.contains("IR_016"))
405            ) {
406                jcbCal = new JComboBox(visCalStrings);
407            } else {
408                jcbCal = new JComboBox(irCalStrings);
409            }
410            display.add(jcbCal);
411        }
412        
413        protected JComponent doMakeContents() {
414            try {
415                JPanel panel = new JPanel(new BorderLayout());
416                panel.add("Center", display);
417                return panel;
418            }
419            catch (Exception e) {
420                System.out.println(e);
421            }
422            return null;
423        }
424        
425        public void applyToDataSelection(DataSelection dataSelection) {
426            try {
427                dataSelection.putProperty("magnification", jcbMag.getSelectedItem());
428                dataSelection.putProperty("calibration", jcbCal.getSelectedItem());
429            } catch (Exception e) {
430                logger.error("Problem applying changes to data selection", e);
431            }
432        }
433    }
434}