001/*
002 * $Id: HRITDataSource.java,v 1.10 2011/03/24 16:06:33 davep Exp $
003 *
004 * This file is part of McIDAS-V
005 *
006 * Copyright 2007-2011
007 * Space Science and Engineering Center (SSEC)
008 * University of Wisconsin - Madison
009 * 1225 W. Dayton Street, Madison, WI 53706, USA
010 * https://www.ssec.wisc.edu/mcidas
011 * 
012 * All Rights Reserved
013 * 
014 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
015 * some McIDAS-V source code is based on IDV and VisAD source code.  
016 * 
017 * McIDAS-V is free software; you can redistribute it and/or modify
018 * it under the terms of the GNU Lesser Public License as published by
019 * the Free Software Foundation; either version 3 of the License, or
020 * (at your option) any later version.
021 * 
022 * McIDAS-V is distributed in the hope that it will be useful,
023 * but WITHOUT ANY WARRANTY; without even the implied warranty of
024 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
025 * GNU Lesser Public License for more details.
026 * 
027 * You should have received a copy of the GNU Lesser Public License
028 * along with this program.  If not, see http://www.gnu.org/licenses.
029 */
030
031package edu.wisc.ssec.mcidasv.data.hrit;
032
033import java.awt.BorderLayout;
034import java.awt.FlowLayout;
035import java.io.File;
036import java.io.IOException;
037import java.rmi.RemoteException;
038import java.util.ArrayList;
039import java.util.Hashtable;
040import java.util.List;
041
042import javax.swing.JComboBox;
043import javax.swing.JComponent;
044import javax.swing.JPanel;
045
046import edu.wisc.ssec.mcidas.Calibrator;
047
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    /** List of sources files */
065    protected List sources;
066
067    public static String request;
068
069    /** List of sources files */
070    protected List adapters;
071    
072    private List categories;
073
074    /** for unpersistence */
075    protected String oldSourceFromBundles;
076    
077    private static final String DATA_DESCRIPTION = "HRIT Data";
078    
079    private static int counter = 1;
080
081    /** children choices */
082    private List myDataChoices = new ArrayList();
083
084    /**
085     * Default constructor
086     */
087    public HRITDataSource() {}
088
089    /**
090     * Construct a new HRIT data source.
091     * @param  descriptor  descriptor for this <code>DataSource</code>
092     * @param  fileName  name of the HRIT segment file to read
093     * @param  properties  hashtable of properties
094     *
095     * @throws VisADException problem creating data
096     */
097    public HRITDataSource(DataSourceDescriptor descriptor,
098                                 String fileName, Hashtable properties)
099            throws VisADException {
100        this(descriptor, Misc.newList(fileName), properties);
101    }
102    
103    /**
104     * Construct a new HRIT data source.
105     * @param  descriptor  descriptor for this <code>DataSource</code>
106     * @param  sources   List of filenames
107     * @param  properties  hashtable of properties
108     *
109     * @throws VisADException problem creating data
110     */
111    public HRITDataSource(DataSourceDescriptor descriptor,
112                                 List newSources, Hashtable properties)
113            throws VisADException {
114        
115        this(descriptor, newSources, DATA_DESCRIPTION, properties);
116        boolean looksOk = false;
117        String dataCategoryStr = "HRIT Data";
118        if ((newSources != null) && (newSources.size() >= 1)) {
119                String fileNameFullPath = (String) newSources.get(0);
120                if ((fileNameFullPath != null) && (fileNameFullPath.length() >= 58)) {
121                        if ((fileNameFullPath.contains("MSG2")) && (fileNameFullPath.endsWith("-__"))) {
122                                String channelStr = fileNameFullPath.substring(fileNameFullPath.lastIndexOf("MSG2") + 13, fileNameFullPath.lastIndexOf("MSG2") + 19);
123                                String timeStr = fileNameFullPath.substring(fileNameFullPath.lastIndexOf("MSG2") + 33, fileNameFullPath.lastIndexOf("MSG2") + 45);
124                                dataCategoryStr = "MSG2 " + channelStr + " " + timeStr;
125                                looksOk = true;
126                        }
127                }
128        }
129        if (looksOk) {
130                DataCategory.createCategory(dataCategoryStr);
131                categories = DataCategory.parseCategories(dataCategoryStr + ";IMAGE");
132        } else {
133                throw new VisADException("Not a decompressed MSG HRIT file");
134        }
135    }    
136
137        /**
138     * Create a HRITDataSource
139     *
140     * @param descriptor The datasource descriptor
141     * @param newSources List of files or urls
142     * @param description The long name
143     * @param properties properties
144     *
145     * @throws VisADException  couldn't create the data
146     */
147    public HRITDataSource(DataSourceDescriptor descriptor, List newSources,
148                           String description, Hashtable properties) 
149            throws VisADException {
150
151        super(descriptor, "HRIT" + counter, "HRIT" + counter, properties);
152        counter++;
153        sources = newSources;
154    }
155
156
157    /**
158     * Can this data source save its data to local disk
159     *
160     * @return can save to local disk
161     */
162    public boolean canSaveDataToLocalDisk() {
163        return !isFileBased() && (getProperty(PROP_SERVICE_HTTP) != null);
164    }
165
166
167    /**
168     * Are we getting data from a file or from server
169     * 
170     * @return is the data from files
171     */
172    protected boolean isFileBased() {
173        if (sources.size() == 0) {
174            return false;
175        }
176        return (new File(sources.get(0).toString())).exists();
177    }
178
179    /**
180     * This is called when the CacheManager detects the need ot clear memory.
181     * It is intended to be overwritten by derived classes that are holding cached
182     * data that is not in the normal putCache facilities provided by this class
183     * since that data is actually managed by the CacheManager
184     */
185    public void clearCachedData() {
186        super.clearCachedData();
187    }
188    
189    /**
190     * Make and insert the <code>DataChoice</code>-s for this
191     * <code>DataSource</code>.
192     */
193    public void doMakeDataChoices() {
194        DataChoice choice = null;
195        
196        for (int i = 0; i < sources.size(); i++) {
197                String fileNameFullPath = (String) sources.get(i);
198                if (fileNameFullPath.contains("MSG2")) {
199                        String channelStr = fileNameFullPath.substring(fileNameFullPath.lastIndexOf("MSG2") + 13, fileNameFullPath.lastIndexOf("MSG2") + 19);
200                        String timeStr = fileNameFullPath.substring(fileNameFullPath.lastIndexOf("MSG2") + 33, fileNameFullPath.lastIndexOf("MSG2") + 45);
201                        String segStr = fileNameFullPath.substring(fileNameFullPath.lastIndexOf("MSG2") + 27, fileNameFullPath.lastIndexOf("MSG2") + 29);
202                        try {
203                                choice = doMakeDataChoice(0, "MSG2 " + channelStr + " " + timeStr + " SEGMENT " + segStr);
204                        } 
205                        catch (Exception e) {
206                                e.printStackTrace();
207                                System.out.println("doMakeDataChoice failed");
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 resolution         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                        nfe.printStackTrace();
302                }
303        }
304
305        // pull out source index 
306        String idxStr = dataChoice.getName().substring(dataChoice.getName().length() - 2, dataChoice.getName().length());
307        
308        Data data = null;
309        
310        String [] files = new String[1];
311        // initialize central wave number string
312        String cwnStr = "006";
313        for (int i = 0; i < sources.size(); i++) {
314                String tmpStr = (String) sources.get(i);
315                cwnStr = tmpStr.substring(tmpStr.lastIndexOf("MSG2") + 16, tmpStr.lastIndexOf("MSG2") + 19);
316                String segStr = tmpStr.substring(tmpStr.lastIndexOf("MSG2") + 27, tmpStr.lastIndexOf("MSG2") + 29);
317                if (segStr.equals(idxStr)) {
318                        files[0] = (String) sources.get(i);
319                }
320        }
321        
322        // match up central wave number with band number index
323        for (int i = 0; i < bandCWN.length; i++) {
324                if (bandCWN[i].equals(cwnStr)) {
325                        bandNum = i;
326                        break;
327                }
328        }
329        
330        String newCal = (String) dataparams.getProperty("calibration");
331        // do checks to only allow valid calibrations here
332        if (newCal != null) {
333                if ((bandNum >= 4) && (bandNum <= 11)) {
334                        if (newCal.equals("RAD")) {
335                                calType = Calibrator.CAL_RAD;
336                        }
337                        if (newCal.equals("TEMP")) {
338                                calType = Calibrator.CAL_TEMP;
339                        }
340                        if (newCal.equals("BRIT")) {
341                                calType = Calibrator.CAL_BRIT;
342                        }
343                } else {
344                        if (newCal.equals("RAD")) {
345                                calType = Calibrator.CAL_RAD;
346                        }
347                        if (newCal.equals("ALB")) {
348                                calType = Calibrator.CAL_ALB;
349                        }
350                        if (newCal.equals("BRIT")) {
351                                calType = Calibrator.CAL_BRIT;
352                        }                       
353                }
354        }
355
356        HRITAdapter ha;
357                try {
358                        ha = new HRITAdapter(files, magFactor, calType, bandNum);
359                        data = ha.getData();
360                } catch (IOException e) {
361                        e.printStackTrace();
362                }
363
364                return data;
365    }
366
367    protected void initDataSelectionComponents(
368                List<DataSelectionComponent> components,
369                final DataChoice dataChoice) {
370
371        try {
372                components.add(new ResolutionSelection(dataChoice));
373        } 
374        catch (Exception e) {
375                e.printStackTrace();
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                        e.printStackTrace();
431                }
432        }
433    }
434}