001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2024
005 * Space Science and Engineering Center (SSEC)
006 * University of Wisconsin - Madison
007 * 1225 W. Dayton Street, Madison, WI 53706, USA
008 * https://www.ssec.wisc.edu/mcidas/
009 * 
010 * All Rights Reserved
011 * 
012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
013 * some McIDAS-V source code is based on IDV and VisAD source code.  
014 * 
015 * McIDAS-V is free software; you can redistribute it and/or modify
016 * it under the terms of the GNU Lesser Public License as published by
017 * the Free Software Foundation; either version 3 of the License, or
018 * (at your option) any later version.
019 * 
020 * McIDAS-V is distributed in the hope that it will be useful,
021 * but WITHOUT ANY WARRANTY; without even the implied warranty of
022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
023 * GNU Lesser Public License for more details.
024 * 
025 * You should have received a copy of the GNU Lesser Public License
026 * along with this program.  If not, see https://www.gnu.org/licenses/.
027 */
028
029package edu.wisc.ssec.mcidasv.data;
030
031import java.awt.BorderLayout;
032import java.awt.Color;
033import java.awt.geom.Rectangle2D;
034import java.awt.event.ComponentEvent;
035import java.awt.event.ComponentListener;
036import java.net.URL;
037import java.rmi.RemoteException;
038
039import javax.swing.JComponent;
040import javax.swing.JPanel;
041
042import visad.BaseColorControl;
043import visad.CellImpl;
044import visad.FlatField;
045import visad.FunctionType;
046import visad.Gridded2DSet;
047import visad.RealTupleType;
048import visad.RealType;
049import visad.ScalarMap;
050import visad.VisADException;
051import visad.data.mcidas.BaseMapAdapter;
052import visad.georef.MapProjection;
053import ucar.visad.display.Displayable;
054import ucar.visad.display.DisplayMaster;
055import ucar.visad.display.LineDrawing;
056import ucar.visad.display.MapLines;
057import ucar.unidata.data.DataChoice;
058import ucar.unidata.data.DataSelection;
059import ucar.unidata.data.DataSourceImpl;
060import ucar.unidata.data.DataSelectionComponent;
061import ucar.unidata.data.GeoSelection;
062import ucar.unidata.data.grid.GridUtil;
063import ucar.unidata.idv.IdvObjectStore;
064import ucar.unidata.idv.MapViewManager;
065import ucar.unidata.idv.ViewManager;
066import ucar.unidata.util.Range;
067import ucar.unidata.view.geoloc.MapProjectionDisplay;
068import ucar.unidata.view.geoloc.MapProjectionDisplayJ3D;
069
070import org.slf4j.Logger;
071import org.slf4j.LoggerFactory;
072
073import edu.wisc.ssec.mcidasv.data.hydra.HydraRGBDisplayable;
074import edu.wisc.ssec.mcidasv.data.hydra.MultiSpectralData;
075import edu.wisc.ssec.mcidasv.control.LambertAEA;
076
077public class GeoPreviewSelection extends DataSelectionComponent {
078
079    private static final Logger logger = LoggerFactory.getLogger(GeoPreviewSelection.class);
080    DataChoice dataChoice;
081    FlatField image;
082    boolean isLL;
083    MapProjection sampleProjection;
084
085    double[] x_coords = new double[2];
086    double[] y_coords = new double[2];
087    MapProjectionDisplayJ3D mapProjDsp;
088    DisplayMaster dspMaster;
089    MapViewManager mvm;
090    IdvObjectStore store;
091
092    final private GeoSubsetRubberBandBox rbb;
093    private int lineMag;
094    private int elementMag;
095
096    private GeoLatLonSelection laloSel;
097
098    private LineDrawing box;
099
100    public GeoPreviewSelection(DataSourceImpl dataSource,
101                               DataChoice dataChoice, FlatField image,
102                               GeoLatLonSelection laLoSel,
103                               MapProjection sample, int lMag, int eMag, boolean showPreview)
104            throws VisADException, RemoteException {
105        super("Region");
106
107        this.dataChoice = dataChoice;
108        this.image = image;
109        this.laloSel = laLoSel;
110        this.sampleProjection = sample;
111
112        if (lMag == 0) lMag = 1;
113        if (eMag == 0) eMag = 1;
114        this.lineMag = lMag;
115        this.elementMag = eMag;
116        sample = getDataProjection();
117
118        if (this.sampleProjection == null) {
119            this.sampleProjection = sample;
120        }
121
122        isLL = sampleProjection.isLatLonOrder();
123        mapProjDsp = new MapProjectionDisplayJ3D(MapProjectionDisplay.MODE_2Din3D);
124        mapProjDsp.enableRubberBanding(false);
125        dspMaster = mapProjDsp;
126        mapProjDsp.setMapProjection(sampleProjection);
127        RealType imageRangeType = (((FunctionType)image.getType()).getFlatRange().getRealComponents())[0];
128        HydraRGBDisplayable imageDsp = new HydraRGBDisplayable("image", imageRangeType, null, true, null);
129
130        String name = this.dataChoice.getName();
131
132        if (showPreview) {
133            if (name.startsWith("186_")) {
134                filterMissingValueABI();
135            }
136            imageDsp.setData(image);
137        }
138
139        MapLines mapLines  = new MapLines("maplines");
140        URL mapSource = mapProjDsp.getClass().getResource("/auxdata/maps/OUTLSUPU");
141        try {
142            BaseMapAdapter mapAdapter = new BaseMapAdapter(mapSource);
143            mapLines.setMapLines(mapAdapter.getData());
144            mapLines.setColor(java.awt.Color.cyan);
145            mapProjDsp.addDisplayable(mapLines);
146        } catch (Exception excp) {
147            logger.error("can't open map file="+mapSource, excp);
148        }
149
150        mapLines  = new MapLines("maplines");
151        mapSource = mapProjDsp.getClass().getResource("/auxdata/maps/OUTLSUPW");
152        try {
153            BaseMapAdapter mapAdapter = new BaseMapAdapter(mapSource);
154            mapLines.setMapLines(mapAdapter.getData());
155            mapLines.setColor(java.awt.Color.cyan);
156            mapProjDsp.addDisplayable(mapLines);
157        } catch (Exception excp) {
158            logger.error("can't open map file="+mapSource, excp);
159        }
160
161        mapLines  = new MapLines("maplines");
162        mapSource = mapProjDsp.getClass().getResource("/auxdata/maps/OUTLHPOL");
163        try {
164            BaseMapAdapter mapAdapter = new BaseMapAdapter(mapSource);
165            mapLines.setMapLines(mapAdapter.getData());
166            mapLines.setColor(java.awt.Color.cyan);
167            mapProjDsp.addDisplayable(mapLines);
168        } catch (Exception excp) {
169            logger.error("can't open map file="+mapSource, excp);
170        }
171
172        if (showPreview) {
173            dspMaster.addDisplayable(imageDsp);
174        }
175        rbb = new GeoSubsetRubberBandBox(isLL, image, ((MapProjectionDisplay)mapProjDsp).getDisplayCoordinateSystem(), 1);
176        mvm = new MapViewManager(dataSource.getDataContext().getIdv());
177        store = dataSource.getDataContext().getIdv().getStore();
178        rbb.setColor((Color)store.get(ViewManager.PREF_FGCOLOR, Color.GREEN));
179        rbb.addAction(new CellImpl() {
180            public void doAction() throws VisADException, RemoteException {
181                eraseBox();
182                forceCoords();
183            }
184        });
185        addRBB();
186        makeBox();
187
188        dspMaster.draw();
189        ScalarMap colorMap = imageDsp.getColorMap();
190        if (showPreview) {
191            Range[] range = GridUtil.fieldMinMax(this.image);
192            Range imageRange = range[0];
193            int max;
194            int min;
195            double dMax = imageRange.getMax();
196            double dMin = imageRange.getMin();
197            DataSelection ds = this.dataChoice.getDataSelection();
198            if (ds != null) {
199                GeoSelection gs = ds.getGeoSelection();
200            }
201            if (name.endsWith("TEMP")) {
202                min = (int)(dMax);
203                max = (int)(dMin);
204            } else {
205                max = (int)(dMin);
206                min = (int)(dMax);
207            }
208            colorMap.setRange(min, max);
209            BaseColorControl clrCntrl = (BaseColorControl) colorMap.getControl();
210            clrCntrl.setTable(BaseColorControl.initTableGreyWedge(new float[4][256], true));
211        }
212    }
213
214    private void filterMissingValueABI() throws VisADException, RemoteException {
215        float missingValue = getMissingValueABI();
216        float[][] values = image.getFloats();
217        for (int i = 0; i < values[0].length; i++) {
218            if (values[0][i] == missingValue) {
219                values[0][i] = Float.NaN;
220            }
221        }
222        image.setSamples(values);
223    }
224
225    private float getMissingValueABI() {
226        String name = this.dataChoice.getName();
227        if (!name.startsWith("186_")) {
228            throw new AssertionError("This method is only applicable to ABI");
229        }
230        if (name.contains("_Band1_")) {
231            return 1023.0f;
232        } else if (name.contains("_Band2_")) {
233            return 4095.0f;
234        } else if (name.contains("_Band3_")) {
235            return 1023.0f;
236        } else if (name.contains("_Band4_")) {
237            return 2047.0f;
238        } else if (name.contains("_Band5_")) {
239            return 1023.0f;
240        } else if (name.contains("_Band6_")) {
241            return 1023.0f;
242        } else if (name.contains("_Band7_")) {
243            return 16383.0f;
244        } else if (name.contains("_Band8_")) {
245            return 4095.0f;
246        } else if (name.contains("_Band9_")) {
247            return 2047.0f;
248        } else if (name.contains("_Band10_")) {
249            return 4095.0f;
250        } else if (name.contains("_Band11_")) {
251            return 4095.0f;
252        } else if (name.contains("_Band12_")) {
253            return 2047.0f;
254        } else if (name.contains("_Band13_")) {
255            return 4095.0f;
256        } else if (name.contains("_Band14_")) {
257            return 4095.0f;
258        } else if (name.contains("_Band15_")) {
259            return 4095.0f;
260        } else if (name.contains("_Band16_")) {
261            return 1023.0f;
262        } else {
263            throw new AssertionError("Could not infer band from '"+name+'\'');
264        }
265    }
266
267    public MapProjection getDataProjection() {
268        MapProjection mp = null;
269        Rectangle2D rect = MultiSpectralData.getLonLatBoundingBox(image);
270        try {
271            mp = new LambertAEA(rect);
272        } catch (Exception e) {
273            logger.error("error while attempting to create new LambertAEA", e);
274        }
275        return mp;
276    }
277
278    public void initBox() {
279        this.drawBox();
280    }
281
282    protected JComponent doMakeContents() {
283        try {
284            JPanel panel = new JPanel(new BorderLayout());
285            panel.add("Center", dspMaster.getDisplayComponent());
286            panel.addComponentListener (new ComponentListener() {
287                public void componentHidden(ComponentEvent ce) {
288                    dspMaster.getDisplayComponent().setVisible(false);
289                }
290                public void componentShown(ComponentEvent ce) {
291                    dspMaster.getDisplayComponent().setVisible(true);
292                    drawBox();
293                    rbb.resetExtremes();
294                }
295                public void componentMoved(ComponentEvent ce) {
296                }
297                public void componentResized(ComponentEvent ce) {
298                }
299            });
300            return panel;
301        }
302        catch (Exception e) {
303            logger.error("error building preview panel", e);
304        }
305        return null;
306    }
307
308    public void setDataChoice(DataChoice choice) {
309        logger.trace("oldChoice={} newChoice={}", this.dataChoice, choice);
310        this.dataChoice = choice;
311    }
312    public DataChoice getDataChoice() {
313        return this.dataChoice;
314    }
315
316    private void forceCoords() {
317        float[] extrms = rbb.getRanges();
318        x_coords[0] = (double)extrms[0];
319        y_coords[0] = (double)extrms[1];
320        x_coords[1] = (double)extrms[2];
321        y_coords[1] = (double)extrms[3];
322
323        int height = (int)(y_coords[1] - y_coords[0]);
324        int width = (int)(x_coords[1] - x_coords[0]);
325        if ((height < 1) || (width < 1)) return;
326
327        if (laloSel != null) {
328            int lineMid = (int)((y_coords[0] + y_coords[1])/2.0 + 0.5);
329            int eleMid = (int)((x_coords[0] + x_coords[1])/2.0 + 0.5);
330            double uLLine = y_coords[1];
331            double uLEle = x_coords[0];
332            if (height < 0) {
333                height *= -1;
334                uLLine = y_coords[0];
335            }
336            if (width < 0) {
337                width *= -1;
338                uLEle = x_coords[1];
339            }
340
341            int line = lineMid;
342            int ele = eleMid;
343            if (laloSel.getPlace().equals(GeoLatLonSelection.PLACE_ULEFT)) {
344                line = (int)Math.floor(uLLine + 0.5);
345                ele = (int)Math.floor(uLEle + 0.5);
346            }
347
348            int linRes = laloSel.getPreviewLineRes();
349            int eleRes = laloSel.getPreviewEleRes();
350
351            height *= linRes;
352            width *= eleRes;
353            laloSel.setBaseNumLines(height);
354            laloSel.setBaseNumElements(width);
355
356            this.lineMag = laloSel.getLineMag();
357            this.elementMag = laloSel.getElementMag();
358            if (lineMag > 0) {
359                height *= lineMag;
360            } else if (lineMag < 0) {
361                height /= -lineMag;
362            }
363            if (elementMag > 0) {
364                width *= elementMag;
365            } else if (elementMag < 0) {
366                width /= -elementMag;
367            }
368
369            Rectangle2D mapArea = sampleProjection.getDefaultMapArea();
370            double previewXDim = mapArea.getWidth();
371            double previewYDim = mapArea.getHeight();
372            double dLin = (double)line;
373            double dEle = (double)ele;
374            if ((line < 0) || (dLin > previewYDim) ||
375                    (ele < 0) || (dEle > previewXDim)) {
376                line = -1;
377                ele = -1;
378            }
379
380//              boolean lock = laloSel.getLockOn();
381//              laloSel.setLockOn(true);
382//              int lineMag = 1;
383//              int eleMag = 1;
384            laloSel.setNumLines(height);
385            laloSel.setNumEles(width);
386//              laloSel.setBaseNumLines(height);
387//              laloSel.setBaseNumElements(width);
388//              laloSel.setLineMag(lineMag);
389//              laloSel.setElementMag(eleMag);
390//              laloSel.lineMagSlider.setValue(lineMag);
391//              laloSel.setLRes(-1.0);
392//              laloSel.elementMagSlider.setValue(eleMag);
393//              laloSel.setERes(-1.0);
394//              laloSel.amUpdating = true;
395//              laloSel.lineMagSliderChanged(false);
396//              laloSel.elementMagSliderChanged(false);
397//              laloSel.amUpdating = false;
398//              laloSel.setLockOn(lock);
399
400            laloSel.getGeoLocationInfo(line, ele);
401            String type = laloSel.getCoordinateType();
402            int pos = 0;
403            if (laloSel.getPlace().equals(GeoLatLonSelection.PLACE_ULEFT)) pos = 1;
404            if (type.equals(GeoLatLonSelection.TYPE_LATLON)) {
405                double[][] pts = laloSel.getLatLonPoints();
406                laloSel.setLatitude(pts[0][pos]);
407                laloSel.setLongitude(pts[1][pos]);
408                laloSel.convertToLineEle();
409            } else {
410                double[][] pts = laloSel.getImagePoints();
411                if (type.equals(GeoLatLonSelection.TYPE_AREA))
412                    pts = laloSel.getAreaPoints();
413                laloSel.setElement((int)Math.floor(pts[0][pos] + 0.5));
414                laloSel.setLine((int)Math.floor(pts[1][pos] + 0.5));
415                laloSel.setLineElement();
416                laloSel.convertToLatLon();
417            }
418        }
419    }
420
421    @Override public void applyToDataSelection(DataSelection dataSelection) {
422    }
423
424    @Override public boolean getShowInControlProperties() {
425        return false;
426    }
427
428    public void drawBox() {
429        if (box == null) makeBox();
430        removeRBB();
431
432        double[][] elelin = laloSel.getDisplayELPoints();
433        if (elelin == null) return;
434
435        for (int i=0; i<2; i++) {
436            for (int j=0; j<5; j++) {
437                Double val = new Double(elelin[i][j]);
438                if (val.isNaN()) {
439                    eraseBox();
440                    return;
441                }
442            }
443        }
444
445        float[][] floatVals = new float[][] {
446                { (float)elelin[0][0], (float)elelin[0][1], (float)elelin[0][2],
447                        (float)elelin[0][3], (float)elelin[0][4] },
448                { (float)elelin[1][0], (float)elelin[1][1], (float)elelin[1][2],
449                        (float)elelin[1][3], (float)elelin[1][4] }};
450
451        float[][] dispVals = new float[][] {
452                { floatVals[0][1], floatVals[0][2], floatVals[0][4],
453                        floatVals[0][3], floatVals[0][1] },
454                { floatVals[1][1], floatVals[1][2], floatVals[1][4],
455                        floatVals[1][3], floatVals[1][1] }
456        };
457
458        try {
459            float[][] refVals = rbb.getDisplayCoordSystem().toReference(dispVals);
460            Gridded2DSet set = new Gridded2DSet(RealTupleType.SpatialCartesian2DTuple,
461                    refVals, 5);
462            box.setData(set);
463        } catch (Exception e) {
464            logger.error("error drawing box", e);
465        }
466    }
467
468    private void makeBox() {
469        if (box == null) {
470            try {
471                box = new LineDrawing("box");
472                box.setColor((Color)store.get(ViewManager.PREF_FGCOLOR, Color.GREEN));
473                dspMaster.addDisplayable(box);
474            } catch (Exception e) {
475                logger.error("error making box", e);
476            }
477        }
478    }
479
480    private void eraseBox() {
481        Gridded2DSet set = null;
482        if (box == null) makeBox();
483        try {
484            set = new Gridded2DSet(RealTupleType.LatitudeLongitudeTuple,
485                    new float[][] {
486                            { (float)0.0, (float)0.0 },
487                            { (float)0.0, (float)0.0 },
488                    }, 2);
489            box.setData(set);
490        } catch (Exception e) {
491            logger.error("error erasing box", e);
492        }
493        addRBB();
494    }
495
496    private boolean rBBPresent() {
497        Displayable[] dsps = dspMaster.getDisplayables();
498        if (dsps.length > 0) {
499            for (int i = 0; i < dsps.length; i++) {
500                Displayable disp = dsps[i];
501                if (disp == (Displayable)rbb) {
502                    return true;
503                }
504            }
505        }
506        return false;
507    }
508
509    private void removeRBB() {
510        if (rBBPresent()) {
511            try {
512                dspMaster.removeDisplayable(rbb);
513            } catch (Exception e) {
514                logger.error("error removing rubberband box", e);
515            }
516        }
517        addRBB();
518    }
519
520    private void addRBB() {
521        if (!rBBPresent()) {
522            try {
523                dspMaster.addDisplayable(rbb);
524            } catch (Exception e) {
525                logger.error("error adding rubberband box", e);
526            }
527        }
528    }
529}