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