001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2017
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
029package edu.wisc.ssec.mcidasv.data;
030
031import java.awt.Image;
032import java.rmi.RemoteException;
033import java.util.ArrayList;
034import java.util.Arrays;
035import java.util.Calendar;
036import java.util.Date;
037import java.util.GregorianCalendar;
038import java.util.Hashtable;
039import java.util.Iterator;
040import java.util.List;
041import java.util.TimeZone;
042
043import ucar.unidata.data.CompositeDataChoice;
044import ucar.unidata.data.DataCategory;
045import ucar.unidata.data.DataChoice;
046import ucar.unidata.data.DataContext;
047import ucar.unidata.data.DataSelection;
048import ucar.unidata.data.DataSelectionComponent;
049import ucar.unidata.data.DataSourceDescriptor;
050import ucar.unidata.data.DataSourceImpl;
051import ucar.unidata.data.DirectDataChoice;
052import ucar.unidata.idv.IntegratedDataViewer;
053import ucar.unidata.idv.control.DisplayControlImpl;
054import ucar.unidata.idv.control.ImageSequenceControl;
055import ucar.unidata.ui.colortable.ColorTableManager;
056import ucar.unidata.util.ColorTable;
057import ucar.unidata.util.Misc;
058import visad.Data;
059import visad.DateTime;
060import visad.FlatField;
061import visad.FunctionType;
062import visad.Linear2DSet;
063import visad.RealTupleType;
064import visad.RealType;
065import visad.VisADException;
066import visad.data.mcidas.AREACoordinateSystem;
067import visad.meteorology.NavigatedImage;
068import visad.meteorology.SingleBandedImage;
069import edu.wisc.ssec.mcidasv.control.FrameComponentInfo;
070import edu.wisc.ssec.mcidasv.control.McIdasComponents;
071import 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 */
079public 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 inFrameNumbers List of frame numbers. Cannot be {@code null}.
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 frame The {@literal "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}