001    /*
002     * $Id: McIdasXDataSource.java,v 1.15 2012/02/19 17:35:45 davep Exp $
003     *
004     * This file is part of McIDAS-V
005     *
006     * Copyright 2007-2012
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    
031    package edu.wisc.ssec.mcidasv.data;
032    
033    import java.awt.Image;
034    import java.rmi.RemoteException;
035    import java.util.ArrayList;
036    import java.util.Arrays;
037    import java.util.Calendar;
038    import java.util.Date;
039    import java.util.GregorianCalendar;
040    import java.util.Hashtable;
041    import java.util.Iterator;
042    import java.util.List;
043    import java.util.TimeZone;
044    
045    import ucar.unidata.data.CompositeDataChoice;
046    import ucar.unidata.data.DataCategory;
047    import ucar.unidata.data.DataChoice;
048    import ucar.unidata.data.DataContext;
049    import ucar.unidata.data.DataSelection;
050    import ucar.unidata.data.DataSelectionComponent;
051    import ucar.unidata.data.DataSourceDescriptor;
052    import ucar.unidata.data.DataSourceImpl;
053    import ucar.unidata.data.DirectDataChoice;
054    import ucar.unidata.idv.IntegratedDataViewer;
055    import ucar.unidata.idv.control.DisplayControlImpl;
056    import ucar.unidata.idv.control.ImageSequenceControl;
057    import ucar.unidata.ui.colortable.ColorTableManager;
058    import ucar.unidata.util.ColorTable;
059    import ucar.unidata.util.Misc;
060    import visad.Data;
061    import visad.DateTime;
062    import visad.FlatField;
063    import visad.FunctionType;
064    import visad.Linear2DSet;
065    import visad.RealTupleType;
066    import visad.RealType;
067    import visad.VisADException;
068    import visad.data.mcidas.AREACoordinateSystem;
069    import visad.meteorology.NavigatedImage;
070    import visad.meteorology.SingleBandedImage;
071    import edu.wisc.ssec.mcidasv.control.FrameComponentInfo;
072    import edu.wisc.ssec.mcidasv.control.McIdasComponents;
073    import 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.15 $
080     */
081    public 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    }