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