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.control;
030
031import java.awt.Color;
032import java.rmi.RemoteException;
033import java.text.DecimalFormat;
034
035import edu.wisc.ssec.mcidasv.display.hydra.MultiSpectralDisplay;
036
037import ucar.unidata.idv.control.LineProbeControl;
038import ucar.unidata.util.LogUtil;
039import ucar.visad.display.TextDisplayable;
040import visad.CellImpl;
041import visad.Data;
042import visad.DataReference;
043import visad.DataReferenceImpl;
044import visad.FlatField;
045import visad.MathType;
046import visad.Real;
047import visad.RealTuple;
048import visad.RealTupleType;
049import visad.Text;
050import visad.TextType;
051import visad.Tuple;
052import visad.TupleType;
053import visad.VisADException;
054import visad.georef.EarthLocationTuple;
055
056public class HydraImageProbe extends LineProbeControl {
057
058    private static final TupleType TUPTYPE = makeTupleType();
059
060    private DataReference positionRef = null;
061
062    private DataReference spectrumRef = null;
063
064    private Color currentColor = Color.MAGENTA;
065
066    private RealTuple currentPosition = null;
067
068    private Tuple locationValue = null;
069
070    private MultiSpectralDisplay display = null;
071
072    private TextDisplayable valueDisplay = null;
073
074    public HydraImageProbe() throws VisADException, RemoteException {
075        super();
076
077        currentPosition = new RealTuple(RealTupleType.Generic2D);
078
079        spectrumRef = new DataReferenceImpl(hashCode() + "_spectrumRef");
080        positionRef = new DataReferenceImpl(hashCode() + "_positionRef");
081
082        valueDisplay = createValueDisplayer(currentColor);
083
084        new Updater();
085    }
086
087    public void setDisplay(final MultiSpectralDisplay disp) throws VisADException, RemoteException {
088        display = disp;
089        display.addRef(spectrumRef, currentColor);
090    }
091
092    // triggered for both position and color changes.
093    protected void probePositionChanged(final RealTuple newPos) {
094        if (display == null)
095            return;
096
097        if (!currentPosition.equals(newPos)) {
098            updatePosition(newPos);
099            updateLocationValue();
100            updateSpectrum();
101            currentPosition = newPos;
102        } 
103
104        Color tmp = getColor();
105        if (!currentColor.equals(tmp)) {
106            updateSpectrumColor(tmp);
107            currentColor = tmp;
108        }
109    }
110
111    public RealTuple getCurrentPosition() {
112        return currentPosition;
113    }
114    
115    public Color getCurrentColor() {
116        return currentColor;
117    }
118    
119    public TextDisplayable getValueDisplay() {
120        return valueDisplay;
121    }
122
123    public DataReference getSpectrumRef() {
124        return spectrumRef;
125    }
126
127    public DataReference getPositionRef() {
128        return positionRef;
129    }
130
131    public Tuple getLocationValue() {
132        return locationValue;
133    }
134
135    private void updateLocationValue() {
136        Tuple tup = null;
137
138        try {
139            RealTuple location = (RealTuple)positionRef.getData();
140            if (location == null)
141                return;
142
143            FlatField image = (FlatField)display.getImageDisplay().getData();
144            if (image == null)
145                return;
146
147            double[] vals = location.getValues();
148            if (vals[1] < -180)
149                vals[1] += 360f;
150
151            if (vals[1] > 180)
152                vals[1] -= 360f;
153
154            RealTuple lonLat = new RealTuple(RealTupleType.SpatialEarth2DTuple, new double[] { vals[1], vals[0] });
155            Real val = (Real)image.evaluate(lonLat, Data.NEAREST_NEIGHBOR, Data.NO_ERRORS);
156            float fval = (float)val.getValue();
157            tup = new Tuple(TUPTYPE, new Data[] { lonLat, new Text(TextType.Generic, Float.toString(fval)) });
158            valueDisplay.setData(tup);
159        } catch (Exception e) {
160            LogUtil.logException("HydraImageProbe.updateLocationValue", e);
161        }
162
163        if (tup != null)
164            locationValue = tup;
165    }
166
167    public void forceUpdateSpectrum() {
168        updateLocationValue();
169        updateSpectrum();
170        updatePosition(currentPosition);
171    }
172
173    private void updateSpectrum() {
174        try {
175            RealTuple tmp = (RealTuple)positionRef.getData();
176            FlatField spectrum = display.getMultiSpectralData().getSpectrum(tmp);
177            spectrumRef.setData(spectrum);
178        } catch (Exception e) {
179            LogUtil.logException("HydraImageProbe.updateSpectrum", e);
180        }
181    }
182
183    protected void updatePosition(final RealTuple position) {
184        double[] vals = position.getValues();
185        try {
186            EarthLocationTuple elt = (EarthLocationTuple)boxToEarth(
187                new double[] { vals[0], vals[1], 1.0 });
188
189            positionRef.setData(elt.getLatLonPoint());
190        } catch (Exception e) {
191            LogUtil.logException("HydraImageProbe.updatePosition", e);
192        }
193    }
194
195    private void updateSpectrumColor(final Color color) {
196        try {
197            display.updateRef(spectrumRef, color);
198            valueDisplay.setColor(color);
199        } catch (Exception e) {
200            LogUtil.logException("HydraImageProbe.updateColor", e);
201        }
202    }
203
204    private static TextDisplayable createValueDisplayer(final Color color) 
205        throws VisADException, RemoteException 
206    {
207        DecimalFormat fmt = new DecimalFormat();
208        fmt.setMaximumIntegerDigits(3);
209        fmt.setMaximumFractionDigits(1);
210
211        TextDisplayable td = new TextDisplayable(TextType.Generic);
212        td.setLineWidth(2f);
213        td.setColor(color);
214        td.setNumberFormat(fmt);
215
216        return td;
217    }
218
219    private static TupleType makeTupleType() {
220        TupleType t = null;
221        try {
222            t = new TupleType(new MathType[] {RealTupleType.SpatialEarth2DTuple, 
223                                              TextType.Generic});
224        } catch (Exception e) {
225            LogUtil.logException("HydraImageProbe.makeTupleType", e);
226        }
227        return t;
228    }
229
230    private class Updater extends CellImpl {
231        public Updater() throws VisADException, RemoteException {
232            this.addReference(positionRef);
233        }
234        
235        public void doAction() {
236
237        }
238    }
239}