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