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;
030
031import java.awt.Image;
032import java.rmi.RemoteException;
033import java.util.ArrayList;
034import java.util.Arrays;
035import java.util.Calendar;
036import java.util.Date;
037import java.util.GregorianCalendar;
038import java.util.Hashtable;
039import java.util.Iterator;
040import java.util.List;
041import java.util.TimeZone;
042
043import org.slf4j.Logger;
044import org.slf4j.LoggerFactory;
045import ucar.unidata.data.CompositeDataChoice;
046import ucar.unidata.data.DataCategory;
047import ucar.unidata.data.DataChoice;
048import ucar.unidata.data.DataContext;
049import ucar.unidata.data.DataSelection;
050import ucar.unidata.data.DataSelectionComponent;
051import ucar.unidata.data.DataSourceDescriptor;
052import ucar.unidata.data.DataSourceImpl;
053import ucar.unidata.data.DirectDataChoice;
054import ucar.unidata.idv.IntegratedDataViewer;
055import ucar.unidata.idv.control.DisplayControlImpl;
056import ucar.unidata.idv.control.ImageSequenceControl;
057import ucar.unidata.ui.colortable.ColorTableManager;
058import ucar.unidata.util.ColorTable;
059import ucar.unidata.util.Misc;
060import visad.Data;
061import visad.DateTime;
062import visad.FlatField;
063import visad.FunctionType;
064import visad.Linear2DSet;
065import visad.RealTupleType;
066import visad.RealType;
067import visad.VisADException;
068import visad.data.mcidas.AREACoordinateSystem;
069import visad.meteorology.NavigatedImage;
070import visad.meteorology.SingleBandedImage;
071import edu.wisc.ssec.mcidasv.control.FrameComponentInfo;
072import edu.wisc.ssec.mcidasv.control.McIdasComponents;
073import edu.wisc.ssec.mcidasv.control.McIdasImageSequenceControl;
074
075/**
076 * Used to cache a data choice and its data.
077 *
078 * @author IDV development team
079 * @version $Revision$
080 */
081public class McIdasXDataSource extends DataSourceImpl  {
082    
083    private static final Logger logger =
084        LoggerFactory.getLogger(McIdasXDataSource.class);
085    
086    /** list of frames to load */
087    private List frameNumbers = new ArrayList();
088
089    /** list of McIDAS-X frames */
090    private List frameList = new ArrayList();
091    
092    /** McIDAS-X connection info */
093    private McIdasXInfo mcidasxInfo;
094
095    /** list of 2D categories */          
096    private List twoDCategories;  
097                    
098    /** list of 2D time series categories */
099    private List twoDTimeSeriesCategories;
100    
101    /** image data arrays */
102    private double values[][] = new double[1][1];
103
104    //private boolean hasImagePreview = false;
105    private boolean hasImagePreview = true;
106    private Image theImage;
107    private int lastPreview = -1;
108    
109    DisplayControlImpl dci;
110
111    /**
112     * Default bean constructor; does nothing
113     */
114    public McIdasXDataSource() {}
115
116    /**
117     * Create a McIdasXDataSource
118     *
119     *
120     * @param descriptor the datasource descriptor
121     * @param name my name
122     * @param properties my properties
123     */
124    public McIdasXDataSource(DataSourceDescriptor descriptor, String name,
125                            Hashtable properties) {
126        super(descriptor, "McIDAS-X", "McIDAS-X", properties);
127/*
128        System.out.println("McIdasXDataSource:");
129        System.out.println("    descriptor=" + descriptor);
130        System.out.println("    name=" + name);
131        System.out.println("    properties=" + properties);
132*/
133        if ((properties == null) || (properties.get(edu.wisc.ssec.mcidasv.chooser.FrameChooser.FRAME_NUMBERS_KEY) == null)) {
134          List frames = new ArrayList();
135          frames.add(new Integer(-1));
136          properties.put(edu.wisc.ssec.mcidasv.chooser.FrameChooser.FRAME_NUMBERS_KEY, frames);
137        }
138
139        this.frameNumbers.clear();
140        this.frameNumbers = getFrameNumbers();
141        
142        String host = (String)properties.get(edu.wisc.ssec.mcidasv.chooser.FrameChooser.REQUEST_HOST);
143        String port = (String)properties.get(edu.wisc.ssec.mcidasv.chooser.FrameChooser.REQUEST_PORT);
144        String key = (String)properties.get(edu.wisc.ssec.mcidasv.chooser.FrameChooser.REQUEST_KEY);
145        mcidasxInfo = new McIdasXInfo(host, port, key);
146        
147        try {
148                this.frameList = makeFrames(this.frameNumbers);
149        } catch (Exception e) {
150            System.out.println("McIdasXDataSource constructor exception: " + e);
151        }
152    }
153    
154    /**
155     * Make a list of McIDAS-X frames
156     *
157     * @param inFrameNumbers List of frame numbers. Cannot be {@code null}.
158     *
159     * @return ImageDataset
160     */
161    public List makeFrames(List inFrameNumbers) {
162        List frames = new ArrayList();
163        Integer frmInt;
164        for (int i = 0; i < inFrameNumbers.size(); i++) {
165          frmInt = (Integer)inFrameNumbers.get(i);
166          frames.add(new McIdasFrame(frmInt.intValue(), mcidasxInfo));
167        }
168//        System.out.println("McIdasXDataSource makeFrames in: " + frameNumbers + ", out: " + frames);
169        return frames;
170    }
171    
172    /**
173     * Get a frame from the frameList based on frame number
174     */
175    public McIdasFrame getFrame(int frameNumber) {
176        McIdasFrame checkFrame;
177        for (int i=0; i<this.frameList.size(); i++) {
178                checkFrame = (McIdasFrame)frameList.get(i);
179                if (checkFrame.getFrameNumber() == frameNumber) {
180                        return(checkFrame);
181                }
182        }
183        return new McIdasFrame();
184    }
185    
186    /**
187     * Set a frame in the framelist based on frame number
188     */
189    public void setFrame(int frameNumber, McIdasFrame inFrame) {
190        McIdasFrame checkFrame;
191        for (int i=0; i<this.frameList.size(); i++) {
192                checkFrame = (McIdasFrame)frameList.get(i);
193                if (checkFrame.getFrameNumber() == frameNumber) {
194                        this.frameList.set(i, inFrame);
195                }
196        }
197    }
198   
199    /**
200     * This is called after this datasource has been fully created
201     * and initialized after being unpersisted by the XmlEncoder.
202     */
203    public void initAfterUnpersistence() {
204        super.initAfterUnpersistence();
205        this.frameNumbers.clear();
206        this.frameNumbers = getFrameNumbers();
207        this.frameList = makeFrames(this.frameNumbers);
208    }
209
210    /**
211     * Gets called after creation. Initialize the connection
212     */
213    public void initAfterCreation() {
214        initConnection();
215    }
216
217    /**
218     * Initialize the connection to McIdas-X.
219     * This gets called when the data source is newly created
220     * or decoded form a bundle.
221     */
222    private void initConnection() {
223      int istat = 0;
224
225      if (istat < 0)
226        setInError(true,"Unable to connect to McIDAS-X");
227    }
228
229    protected boolean shouldCache(Data data) {
230        return false;
231    }
232    
233    protected void initDataSelectionComponents(
234            List<DataSelectionComponent> components, final DataChoice dataChoice) {
235
236        getDataContext().getIdv().showWaitCursor();
237        makePreviewImage(dataChoice);
238        if (hasImagePreview) {
239                try {
240                        components.add(new ImagePreviewSelection(theImage));
241                } catch (Exception e) {
242                        logger.error("Can't make preview image", e);
243                }
244        }
245        getDataContext().getIdv().showNormalCursor();
246    }
247
248    private void makePreviewImage(DataChoice dataChoice) {
249        int dataFrame = -1;
250        if (dataChoice.getDescription().indexOf("Frame ") >= 0) {
251                try {
252                        dataFrame = Integer.parseInt(dataChoice.getDescription().substring(6));
253                }
254                catch (Exception e) {
255                        hasImagePreview = false;
256                        return;
257                }
258        }
259        if (dataFrame <= 0) {
260                hasImagePreview = false;
261                return;
262        }
263        if (dataFrame != lastPreview) {
264                McIdasFrame mxf = new McIdasFrame(dataFrame, mcidasxInfo);
265                theImage = mxf.getGIF();
266        }
267        hasImagePreview = true;
268        lastPreview = dataFrame;
269    }
270      
271    /**
272     *
273     * @param dataChoice        The data choice that identifies the requested
274     *                          data.
275     * @param category          The data category of the request.
276     * @param dataSelection     Identifies any subsetting of the data.
277     * @param requestProperties Hashtable that holds any detailed request
278     *                          properties.
279     *
280     * @return The data
281     *
282     * @throws RemoteException    Java RMI problem
283     * @throws VisADException     VisAD problem
284     */
285    protected Data getDataInner(DataChoice dataChoice, DataCategory category,
286                                DataSelection dataSelection, Hashtable requestProperties)
287                                throws VisADException, RemoteException {
288/*
289        System.out.println("McIdasXDataSource getDataInner:");
290        System.out.println("   dataChoice=" + dataChoice);
291        System.out.println("   category=" + category);
292        System.out.println("   dataSelection=" + dataSelection);
293        System.out.println("   requestProperties=" + requestProperties);
294*/
295        
296        // Read the properties to decide which frame components should be requested
297        FrameComponentInfo frameComponentInfo = new FrameComponentInfo();
298        Boolean mc;
299        mc = (Boolean)(requestProperties.get(McIdasComponents.IMAGE));
300        if (mc == null)  mc=Boolean.TRUE; 
301        frameComponentInfo.setIsImage(mc.booleanValue());
302        mc = (Boolean)(requestProperties.get(McIdasComponents.GRAPHICS));
303        if (mc == null)  mc=Boolean.TRUE; 
304        frameComponentInfo.setIsGraphics(mc.booleanValue());
305        mc = (Boolean)(requestProperties.get(McIdasComponents.COLORTABLE));
306        if (mc == null)  mc=Boolean.TRUE; 
307        frameComponentInfo.setIsColorTable(mc.booleanValue());
308        mc = (Boolean)(requestProperties.get(McIdasComponents.ANNOTATION));
309        if (mc == null)  mc=Boolean.TRUE; 
310        frameComponentInfo.setIsAnnotation(mc.booleanValue());
311        mc = (Boolean)(requestProperties.get(McIdasComponents.FAKEDATETIME));
312        if (mc == null)  mc=Boolean.TRUE; 
313        frameComponentInfo.setFakeDateTime(mc.booleanValue());
314        
315        List defList = null;
316        frameNumbers = (List)getProperty(edu.wisc.ssec.mcidasv.chooser.FrameChooser.FRAME_NUMBERS_KEY, defList);
317        
318        // Read the properties to decide which frame components need to be requested
319        FrameDirtyInfo frameDirtyInfo = new FrameDirtyInfo();
320        List frameDirtyInfoList = new ArrayList();
321        frameDirtyInfoList = (ArrayList)(requestProperties.get(McIdasComponents.DIRTYINFO));
322        
323        if (frameDirtyInfoList == null) {
324                frameDirtyInfoList = new ArrayList();
325                for (int i=0; i<frameNumbers.size(); i++) {
326                        frameDirtyInfo = new FrameDirtyInfo((Integer)frameNumbers.get(i), true, true, true);
327                        frameDirtyInfoList.add(frameDirtyInfo);
328                }
329        }
330
331        Data data=null;
332        if (frameNumbers.size() < 1) {
333                return data;
334        }
335        if (frameNumbers.size() < 2) {
336                for (int i=0; i<frameDirtyInfoList.size(); i++) {
337                        frameDirtyInfo = (FrameDirtyInfo)frameDirtyInfoList.get(i);
338                        if (frameDirtyInfo.getFrameNumber() == (Integer)frameNumbers.get(0)) {
339//                              System.out.println("frameDirtyInfo: " + frameDirtyInfo);
340                        data = (Data) getMcIdasSequence((Integer)frameNumbers.get(0), frameComponentInfo, frameDirtyInfo);
341                        }
342                }
343        } else {
344                String dc="";
345                String fd="";
346                for (int i=0; i<frameNumbers.size(); i++) {
347                        dc = dataChoice.toString();
348                        fd = (this.frameList.get(i)).toString();
349                        if (dc.compareTo(fd) == 0) {
350                                if (i > 0) {
351                                        frameComponentInfo.setIsColorTable(false);
352                                }
353                        for (int j=0; j<frameDirtyInfoList.size(); j++) {
354                                frameDirtyInfo = (FrameDirtyInfo)frameDirtyInfoList.get(j);
355                                if (frameDirtyInfo.getFrameNumber() == (Integer)frameNumbers.get(i)) {
356//                                      System.out.println("frameDirtyInfo: " + frameDirtyInfo);
357                                data = (Data) getMcIdasSequence((Integer)frameNumbers.get(i), frameComponentInfo, frameDirtyInfo);
358                                }
359                        }
360                        }
361                }
362        }
363        return data;
364    }
365
366    /**
367     * make a time series from selected McIdas-X frames
368     */
369    private SingleBandedImage getMcIdasSequence(int frameNumber,
370                FrameComponentInfo frameComponentInfo,
371                FrameDirtyInfo frameDirtyInfo)
372            throws VisADException, RemoteException {
373/*
374      System.out.println("McIdasXDataSource getMcIdasSequence:");
375      System.out.println("   frmNo=" + frmNo);
376      System.out.println("   frameComponentInfo=" + frameComponentInfo);
377*/
378      SingleBandedImage image = getMcIdasFrame(frameNumber, frameComponentInfo, frameDirtyInfo);
379      if (image != null) {
380         if (shouldCache((Data)image)) {
381            Integer fo = new Integer(frameNumber);
382            putCache(fo,image);
383         }
384      }
385      return image;
386    }
387
388    private DisplayControlImpl getDisplayControlImpl() {
389      dci = null;
390      List dcl = getDataChangeListeners();
391      if (dcl != null) {
392        for (int i=0; i< dcl.size(); i++) {
393          if (dcl.get(i) instanceof McIdasImageSequenceControl) {
394            dci= (DisplayControlImpl)(dcl.get(i));
395            break;
396          }
397        }
398      }
399      return dci;
400    }
401
402    /**
403     * Get frame numbers
404     *
405     * @return frame numbers 
406     */
407    public List getFrameNumbers() {
408        List defList = null;
409        List gotFrameNumbers = (List)getProperty(edu.wisc.ssec.mcidasv.chooser.FrameChooser.FRAME_NUMBERS_KEY, defList);
410        return gotFrameNumbers;
411    }
412
413    /**
414     * Get the name for the main data object
415     *
416     * @return name of main data object
417     */
418    public String getDataName() {
419        String dataName = (String) getProperty(edu.wisc.ssec.mcidasv.chooser.FrameChooser.DATA_NAME_KEY, "Frame Sequence");
420        if (dataName.equals("")) {
421                dataName = "Frame Sequence";
422        }
423        return dataName;
424    }
425    
426    /**
427     * Get McIdasXInfo object
428     * 
429     * @return mcidasxInfo
430     */
431    public McIdasXInfo getMcIdasXInfo() {
432        return mcidasxInfo;
433    }
434
435    /**
436     * Initialize the {@link DataCategory} objects that this data source uses.
437     */
438    private void makeCategories() {
439        twoDTimeSeriesCategories = DataCategory.parseCategories("MCIDASX;", false);
440        twoDCategories = DataCategory.parseCategories("MCIDASX;", false);
441    }
442
443    /**
444     * Return the list of {@link DataCategory} used for single time step data.
445     *
446     * @return A list of categories.
447     */
448    public List getTwoDCategories() {
449        if (twoDCategories == null) {
450            makeCategories();
451        }
452        return twoDCategories;
453    }
454
455    /**
456     * Return the list of {@link DataCategory} used for multiple time step
457     * data.
458     *
459     * @return A list of categories.
460     */
461
462    public List getTwoDTimeSeriesCategories() {
463        if (twoDCategories == null) {
464            makeCategories();
465        }
466        return twoDTimeSeriesCategories;
467    }
468
469
470    /**
471     * Create the set of {@link DataChoice} that represent the data held by
472     * this data source.
473     *
474     * <p>We create one top-level {@link CompositeDataChoice} that represents
475     * all of the image time steps. We create a set of children
476     * {@link DirectDataChoice}, one for each time step.</p>
477     */
478    public void doMakeDataChoices() {
479        if (this.frameList == null) return;
480        CompositeDataChoice composite = new CompositeDataChoice(this,
481                                            getFrameNumbers(), getName(),
482                                            getDataName(),
483                                            (this.frameList.size() > 1)
484                                            ? getTwoDTimeSeriesCategories()
485                                            : getTwoDCategories()) {
486            public List getSelectedDateTimes() {
487                return dataSource.getSelectedDateTimes();
488            }
489        };
490        addDataChoice(composite);
491        doMakeDataChoices(composite);
492    }
493
494    /**
495     * Make the data choices and add them to the given composite
496     *
497     * @param composite The parent data choice to add to
498     */
499    private void doMakeDataChoices(CompositeDataChoice composite) {
500        int cnt = 0;
501        List frameNos = new ArrayList();
502        List frameChoices = new ArrayList();
503        for (Iterator iter = frameList.iterator(); iter.hasNext(); ) {
504            Object              object     = iter.next();
505            McIdasFrame                 frame      = getFrame(object);
506            String              name       = frame.toString();
507            DataSelection       frameSelect = null;
508            Integer frameNo = frame.getFrameNumber();
509            if (frameNo != null) {
510                frameNos.add(frameNo);
511                //We will create the  data choice with an index, not with the actual frame number.
512                frameSelect = new DataSelection(Misc.newList(new Integer(cnt)));
513            }
514            frameSelect = null;
515            DataChoice choice =
516                new DirectDataChoice(this, new FrameDataInfo(cnt, frame),
517                                     composite.getName(), name,
518                                     getTwoDCategories(), frameSelect);
519            cnt++;
520            frameChoices.add(choice);
521        }
522
523        //Sort the data choices.
524        composite.replaceDataChoices(sortChoices(frameChoices));
525    }
526
527    /**
528     * Sort the list of data choices on their frame numbers 
529     *
530     * @param choices The data choices
531     *
532     * @return The data choices sorted
533     */
534    private List sortChoices(List choices) {
535        Object[]   choicesArray = choices.toArray();
536/*
537        Comparator comp         = new Comparator() {
538            public int compare(Object o1, Object o2) {
539                McIdasFrameDescriptor fd1 = getDescriptor(o1);
540                McIdasFrameDescriptor fd2 = getDescriptor(o2);
541                return fd1.getFrameNumber().compareTo(fd2.getFrameNumber());
542            }
543        };
544        Arrays.sort(choicesArray, comp);
545*/
546        return new ArrayList(Arrays.asList(choicesArray));
547    }
548
549    /**
550     * A utility method that helps us deal with legacy bundles that used to
551     * have String file names as the id of a data choice.
552     *
553     * @param object     May be an AddeImageDescriptor (for new bundles) or a
554     *                   String that is converted to an image descriptor.
555     * @return The image descriptor.
556     */
557    private McIdasFrame getFrame(Object object) {
558        if (object == null) {
559            return null;
560        }
561
562        if (object instanceof McIdasFrame) {
563                return (McIdasFrame) object;
564        }
565        
566        return new McIdasFrame();
567    }
568
569    /**
570     * Class FrameDataInfo Holds an index and a McIdasFrame
571     */
572    public class FrameDataInfo {
573
574        /** The index */
575        private int index;
576
577        /** The FD */
578        private McIdasFrame frame;
579
580        /**
581         * Ctor for xml encoding
582         */
583        public FrameDataInfo() {}
584
585        /**
586         * CTOR
587         *
588         * @param index The index
589         * @param frame The {@literal "FD"}.
590         */
591        public FrameDataInfo(int index, McIdasFrame frame) {
592            this.index = index;
593            this.frame = frame;
594        }
595
596        /**
597         * Get the index
598         *
599         * @return The index
600         */
601        public int getIndex() {
602            return index;
603        }
604
605        /**
606         * Set the index
607         *
608         * @param v The index
609         */
610        public void setIndex(int v) {
611            index = v;
612        }
613        
614        /**
615         * Get the frame
616         *
617         * @return The frame
618         */
619        public McIdasFrame getFrame() {
620            return frame;
621        }
622
623        /**
624         * Set the frame
625         *
626         * @param v The frame
627         */
628        public void setFrame(McIdasFrame v) {
629            frame = v;
630        }
631
632        /**
633         * toString
634         *
635         * @return toString
636         */
637        public String toString() {
638            return "index: " + index + ", frame: " + frame.getFrameNumber();
639        }
640
641    }
642
643    public SingleBandedImage getMcIdasFrame(int frameNumber,
644                FrameComponentInfo frameComponentInfo,
645                FrameDirtyInfo frameDirtyInfo)
646           throws VisADException, RemoteException {
647/*
648        System.out.println("McIdasXDataSource getMcIdasFrame:");
649        System.out.println("   frameNumber=" + frameNumber);
650        System.out.println("   frameComponentInfo=" + frameComponentInfo);
651        System.out.println("   frameDirtyInfo=" + frameDirtyInfo);
652*/
653        FlatField image_data = null;
654        SingleBandedImage field = null;
655
656        if (frameNumber < 1) return field;
657        
658        // Get the appropriate frame out of the list
659        McIdasFrame frm = getFrame(frameNumber);
660        
661        // Tell the frame once whether or not to refresh cached data
662        frm.setRefreshData(frameDirtyInfo.getDirtyImage() || frameDirtyInfo.getDirtyColorTable());
663
664        FrameDirectory fd = frm.getFrameDirectory(frameDirtyInfo.getDirtyImage());
665        int[] nav = fd.getFrameNav();
666        int[] aux = fd.getFrameAux();
667        
668        if (nav[0] == 0) return field;
669        
670        // Set the time of the frame.  Because IDV uses time-based ordering, give the user the option
671        // of "faking" the date/time by using frame number for year.  This preserves -X frame ordering.
672        Date nominal_time;
673        if (!frameComponentInfo.getFakeDateTime()) {
674            nominal_time = fd.getNominalTime();
675        }
676        else {
677            Calendar calendarDate = new GregorianCalendar(frameNumber, Calendar.JANUARY, 1, 0, 0, 0);
678            calendarDate.setTimeZone(TimeZone.getTimeZone("UTC"));
679            nominal_time = calendarDate.getTime();
680        }
681
682        int height = frm.getLineSize(frameDirtyInfo.getDirtyImage());
683        if (height < 0) return field;
684        int width = frm.getElementSize(frameDirtyInfo.getDirtyImage());
685        if (width < 0) return field;
686       
687        // check for frameComponentInfo.isColorTable == true
688        if (frameComponentInfo.getIsColorTable()) {
689            DataContext dataContext = getDataContext();
690            ColorTableManager colorTableManager = ((IntegratedDataViewer)dataContext).getColorTableManager();
691            List dcl = ((IntegratedDataViewer)dataContext).getDisplayControls();
692            DisplayControlImpl dc = null;
693            for (int i=dcl.size()-1; i>=0; i--) {
694                DisplayControlImpl dci = (DisplayControlImpl)dcl.get(i);
695                if (dci instanceof ImageSequenceControl) {
696                    dc = dci;
697                    break;
698                }
699            }
700            ColorTable mcidasXColorTable = frm.getColorTable(frameDirtyInfo.getDirtyColorTable());
701            // TODO: Add a transparent value to the color table when only graphics were requested 
702/*
703            // if image wasn't requested, make color table with entry 0 as transparent
704            if (!frameComponentInfo.getIsImage()) {
705                float[][] mcidasXColorTableAlpha = mcidasXColorTable.getAlphaTable();
706                mcidasXColorTableAlpha[3][0] = 0.0f;
707                mcidasXColorTable.setTable(mcidasXColorTableAlpha);
708            }
709*/
710            colorTableManager.addUsers(mcidasXColorTable);
711            dc.setColorTable("default", mcidasXColorTable);
712        }
713
714        // check for frameComponentInfo.isAnnotation == true
715        int skip = 0;
716        if (!frameComponentInfo.getIsAnnotation()) {
717                skip = 12;
718        }
719        height = height - skip;
720        
721        values = new double[1][width*height];
722
723        // check for frameComponentInfo.isImage == true
724        if (frameComponentInfo.getIsImage()) {
725                byte[] image = frm.getImageData(frameDirtyInfo.getDirtyImage());
726                double pixel;
727                for (int i=0; i<width*height; i++) {
728                        pixel = (double)image[i];
729                        if (pixel < 0.0) pixel += 256.0;
730                        values[0][i] = pixel;
731                }
732        }
733        else {
734                for (int i=0; i<width*height; i++) {
735                        // TODO: Use a special value that is transparent in the color table
736                        values[0][i] = 0.0;
737                }
738        }
739        
740        // check for frameComponentInfo.isGraphics == true
741        if (frameComponentInfo.getIsGraphics()) {
742                byte[] graphics = frm.getGraphicsData(frameDirtyInfo.getDirtyGraphics());
743                for (int i=0; i<width*height; i++) {
744                        if (graphics[i] != (byte)255) {
745                                values[0][i] = (double)graphics[i];
746                        }
747                }
748        }
749        
750        // Done working with the frame, put it back in the list
751        setFrame(frameNumber, frm);
752
753        // fake an area directory
754        int[] adir = new int[64];
755        adir[5] = fd.getULLine();
756        adir[6] = fd.getULEle();
757        adir[8] = height;
758        adir[9] = width;
759        adir[11] = fd.getLineRes();
760        adir[12] = fd.getEleRes();
761
762        AREACoordinateSystem cs;
763        try {
764            cs = new AREACoordinateSystem( adir, nav, aux);
765        } catch (Exception e) {
766            System.out.println("AREACoordinateSystem exception: " + e);
767            return field;
768        }
769
770/*
771        double[][] linele = new double[2][4];
772        double[][] latlon = new double[2][4];
773       // LR
774        linele[0][0] = (double)(width-1);
775        linele[1][0] = 0.0;
776       // UL
777        linele[0][1] = 0.0;
778        linele[1][1] = (double)(height-1);
779       // LL
780        linele[0][2] = 0.0;
781        linele[1][2] = 0.0;
782       // UR
783        linele[0][3] = (double)(width-1);
784        linele[1][3] = (double)(height-1);
785                                                                                              
786        latlon = cs.toReference(linele);
787        System.out.println("linele: " + linele[0][0] + " " + linele[1][0] + " " +
788                           linele[0][1] + " " + linele[1][1] + " " +
789                           linele[0][2] + " " + linele[1][2] + " " +
790                           linele[0][3] + " " + linele[1][3]);
791        System.out.println("latlon: " + latlon[0][0] + " " + latlon[1][0] + " " +
792                           latlon[0][1] + " " + latlon[1][1] + " " +
793                           latlon[0][2] + " " + latlon[1][2] + " " +
794                           latlon[0][3] + " " + latlon[1][3]);
795*/
796 
797        RealType[] domain_components = {RealType.getRealType("ImageElement", null, null),
798              RealType.getRealType("ImageLine", null, null)};
799        RealTupleType image_domain =
800                   new RealTupleType(domain_components, cs, null);
801
802                //  Image numbering is usually the first line is at the "top"
803                //  whereas in VisAD, it is at the bottom.  So define the
804                //  domain set of the FlatField to map the Y axis accordingly
805        Linear2DSet domain_set = new Linear2DSet(image_domain,
806                                 0, (width - 1), width,
807                                 (height - 1), 0, height );
808        RealType range = RealType.getRealType("brightness");
809                                                                                              
810        FunctionType image_func = new FunctionType(image_domain, range);
811                                                                                              
812        // now, define the Data objects
813        image_data = new FlatField(image_func, domain_set);
814        DateTime date = new DateTime(nominal_time);
815        image_data = new NavigatedImage(image_data, date, "McIdas Image");
816
817        // put the data values into the FlatField image_data
818        image_data.setSamples(values,false);
819        field = (SingleBandedImage) image_data;
820
821        return field;
822    }
823    
824}