001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2018
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.hydra;
030
031import java.awt.BorderLayout;
032import java.awt.Color;
033import java.awt.FlowLayout;
034import java.awt.event.ActionEvent;
035import java.awt.event.ActionListener;
036import java.net.URL;
037import java.rmi.RemoteException;
038import java.util.HashMap;
039import java.util.Hashtable;
040import java.util.Map;
041
042import javax.swing.DefaultBoundedRangeModel;
043import javax.swing.JComponent;
044import javax.swing.JLabel;
045import javax.swing.JPanel;
046import javax.swing.JSlider;
047import javax.swing.JTextField;
048import javax.swing.event.ChangeEvent;
049import javax.swing.event.ChangeListener;
050
051import org.slf4j.Logger;
052import org.slf4j.LoggerFactory;
053
054import ucar.unidata.data.DataChoice;
055import ucar.unidata.data.DataSelection;
056import ucar.unidata.data.DataSelectionComponent;
057import ucar.unidata.geoloc.ProjectionRect;
058import ucar.unidata.geoloc.projection.LatLonProjection;
059import ucar.unidata.view.geoloc.MapProjectionDisplayJ2D;
060import ucar.visad.ProjectionCoordinateSystem;
061import ucar.visad.display.DisplayMaster;
062import ucar.visad.display.LineDrawing;
063import ucar.visad.display.MapLines;
064
065import visad.FlatField;
066import visad.Gridded2DSet;
067import visad.GriddedSet;
068import visad.RealTupleType;
069import visad.VisADException;
070import visad.data.mcidas.BaseMapAdapter;
071import visad.georef.MapProjection;
072
073public class TrackSelection extends DataSelectionComponent {
074
075    private static final Logger logger = LoggerFactory.getLogger(TrackSelection.class);
076
077    public static final int DEFAULT_TRACK_STRIDE = 5;
078    public static final int DEFAULT_VERTICAL_STRIDE = 2;
079    public static final int DEFAULT_TRACK_LENGTH_PERCENT = 5;
080
081    DataChoice dataChoice;
082    FlatField track;
083
084    double[] x_coords = new double[2];
085    double[] y_coords = new double[2];
086    MapProjectionDisplayJ2D mapProjDsp;
087    DisplayMaster dspMaster;
088
089    int trackStride;
090    int verticalStride;
091
092    JTextField trkStr;
093    JTextField vrtStr;
094    JTextField lengthField;
095
096    LineDrawing trackSelectDsp;
097    float[][] trackLocs;
098    int trackLen;
099    int trackPos;
100    int trackStart;
101    int trackStop;
102    int trackLengthPercent = DEFAULT_TRACK_LENGTH_PERCENT;
103    int selectWidth;
104    Map defaultSubset;
105
106    TrackSelection(DataChoice dataChoice, FlatField track, Map defaultSubset)
107            throws VisADException, RemoteException {
108        super("Track");
109        this.dataChoice = dataChoice;
110        this.track = track;
111        this.defaultSubset = defaultSubset;
112
113        GriddedSet gset = (GriddedSet) track.getDomainSet();
114        float[] lo = gset.getLow();
115        float[] hi = gset.getHi();
116        float[][] values = gset.getSamples();
117
118        trackLen = values[0].length;
119        selectWidth = (int) (trackLen * ((float) trackLengthPercent / 100.0f));
120        selectWidth /= 2;
121        trackPos = trackLen / 2;
122        trackStart = trackPos - selectWidth;
123        trackStop = trackPos + selectWidth;
124
125        trackLocs = values;
126        Gridded2DSet track2D = new Gridded2DSet(RealTupleType.SpatialEarth2DTuple, new float[][] {
127                values[0], values[1] }, values[0].length);
128
129        mapProjDsp = new MapProjectionDisplayJ2D();
130
131        dspMaster = mapProjDsp;
132        mapProjDsp.setMapProjection(getDataProjection(new ProjectionRect(lo[0], lo[1], hi[0], hi[1])));
133        LineDrawing trackDsp = new LineDrawing("track");
134        trackDsp.setLineWidth(0.5f);
135        trackDsp.setData(track2D);
136
137        trackSelectDsp = new LineDrawing("trackSelect");
138        trackSelectDsp.setLineWidth(3f);
139        trackSelectDsp.setColor(java.awt.Color.magenta);
140
141        updateTrackSelect();
142    
143        mapProjDsp.addDisplayable(trackSelectDsp);
144        mapProjDsp.addDisplayable(trackDsp);
145
146        MapLines mapLines = new MapLines("maplines");
147        URL mapSource = mapProjDsp.getClass().getResource("/auxdata/maps/OUTLSUPU");
148        try {
149            BaseMapAdapter mapAdapter = new BaseMapAdapter(mapSource);
150            mapLines.setMapLines(mapAdapter.getData());
151            mapLines.setColor(Color.cyan);
152        } catch (Exception excp) {
153            logger.error("cannot open map file: " + mapSource, excp);
154        }
155
156        mapLines = new MapLines("maplines");
157        mapSource = mapProjDsp.getClass().getResource("/auxdata/maps/OUTLSUPW");
158        try {
159            BaseMapAdapter mapAdapter = new BaseMapAdapter(mapSource);
160            mapLines.setMapLines(mapAdapter.getData());
161            mapLines.setColor(Color.cyan);
162            mapProjDsp.addDisplayable(mapLines);
163        } catch (Exception excp) {
164            logger.error("cannot open map file: " + mapSource, excp);
165        }
166
167        mapLines = new MapLines("maplines");
168        mapSource = mapProjDsp.getClass().getResource("/auxdata/maps/OUTLHPOL");
169        try {
170            BaseMapAdapter mapAdapter = new BaseMapAdapter(mapSource);
171            mapLines.setMapLines(mapAdapter.getData());
172            mapLines.setColor(Color.cyan);
173        } catch (Exception excp) {
174            logger.error("cannot open map file: " + mapSource, excp);
175        }
176
177        dspMaster.draw();
178    }
179
180    public MapProjection getDataProjection(ProjectionRect rect) {
181        MapProjection mp = null;
182        try {
183            mp = new ProjectionCoordinateSystem(new LatLonProjection("blah", rect));
184        } catch (Exception e) {
185            logger.error("error getting data projection", e);
186        }
187        return mp;
188    }
189
190    protected JComponent doMakeContents() {
191        try {
192            JPanel panel = new JPanel(new BorderLayout());
193            panel.add("Center", dspMaster.getDisplayComponent());
194
195            JPanel stridePanel = new JPanel(new FlowLayout());
196            
197            // initialize the UI components
198            trkStr = new JTextField(Integer.toString(TrackSelection.DEFAULT_TRACK_STRIDE), 2);
199            vrtStr = new JTextField(Integer.toString(TrackSelection.DEFAULT_VERTICAL_STRIDE), 2);
200            lengthField = new JTextField(Integer.toString(TrackSelection.DEFAULT_TRACK_LENGTH_PERCENT), 2);
201            
202            // set tooltip hints
203            trkStr.setToolTipText("Sets the horizontal stride along the track (X/Y-axes)");
204            vrtStr.setToolTipText("Sets the vertical stride (Z-axis)");
205            lengthField.setToolTipText("Sets the percentage length of total track to display");
206
207            trkStr.addActionListener(new ActionListener() {
208                public void actionPerformed(ActionEvent ae) {
209                    setTrackStride(Integer.valueOf(trkStr.getText().trim()));
210                }
211            });
212            vrtStr.addActionListener(new ActionListener() {
213                public void actionPerformed(ActionEvent ae) {
214                    setVerticalStride(Integer.valueOf(vrtStr.getText().trim()));
215                }
216            });
217            lengthField.addActionListener(new ActionListener() {
218                public void actionPerformed(ActionEvent ae) {
219                    setLengthPercent(Integer.valueOf(lengthField.getText().trim()));
220                }
221            });
222
223            stridePanel.add(new JLabel("Track Stride:"));
224            stridePanel.add(trkStr);
225            stridePanel.add(new JLabel("Vertical Stride:"));
226            stridePanel.add(vrtStr);
227            stridePanel.add(new JLabel("Length %:"));
228            stridePanel.add(lengthField);
229
230            JPanel selectPanel = new JPanel(new BorderLayout());
231            DefaultBoundedRangeModel brm = new DefaultBoundedRangeModel(trackStart, 0, 0, trackLen);
232            JSlider trackSelect = new JSlider(brm);
233            trackSelect
234                    .setToolTipText("<html>"
235                            + "Sets the location of the track to display (with respect to Length % below). <br>"
236                            + "The slider represents the middle of the length to be plotted.  The left end of <br>"
237                            + "the slider is the beginning of the track, and the right is the end. The portion <br>"
238                            + "of the track to be displayed is outlined in magenta." + "</html>");
239            Hashtable<Integer, JLabel> labelTable = new Hashtable<Integer, JLabel>();
240            labelTable.put(0, new JLabel("Track Start"));
241            labelTable.put(trackLen, new JLabel("Track End"));
242            trackSelect.setLabelTable(labelTable);
243            trackSelect.setPaintLabels(true);
244            trackSelect.addChangeListener(new ChangeListener() {
245                public void stateChanged(ChangeEvent e) {
246                    trackPos = (int) ((JSlider) e.getSource()).getValue();
247                    updateTrackSelect();
248                }
249            });
250            selectPanel.add(trackSelect, BorderLayout.NORTH);
251            selectPanel.add(stridePanel, BorderLayout.SOUTH);
252            panel.add(selectPanel, BorderLayout.SOUTH);
253
254            return panel;
255        } catch (Exception e) {
256            logger.error("error creating contents", e);
257        }
258        return null;
259    }
260
261    public void setTrackStride(int stride) {
262        trackStride = stride;
263    }
264
265    public void setVerticalStride(int stride) {
266        verticalStride = stride;
267    }
268
269    public void setLengthPercent(int percent) {
270        trackLengthPercent = percent;
271        selectWidth = (int) (trackLen * ((float) trackLengthPercent / 100.0f));
272        selectWidth /= 2;
273        updateTrackSelect();
274    }
275
276    /**
277     * Update Track Stride if input text box holds a positive integer.
278     * 
279     * @return true if trackStride was updated
280     */
281
282    public boolean setTrackStride() {
283        boolean setOk = false;
284        try {
285            int newStride = Integer.valueOf(trkStr.getText().trim());
286            if (newStride >= 1) {
287                trackStride = newStride;
288                setOk = true;
289            } else {
290                setOk = false;
291            }
292        } catch (NumberFormatException nfe) {
293            // do nothing, will return correct result code
294        }
295        return setOk;
296    }
297
298    /**
299     * Update Vertical Stride if input text box holds a positive integer.
300     * 
301     * @return true if verticalStride was updated
302     */
303
304    public boolean setVerticalStride() {
305        boolean setOk = false;
306        try {
307            int newStride = Integer.valueOf(vrtStr.getText().trim());
308            if (newStride >= 1) {
309                verticalStride = newStride;
310                setOk = true;
311            } else {
312                setOk = false;
313            }
314        } catch (NumberFormatException nfe) {
315            // do nothing, will return correct result code
316        }
317        return setOk;
318    }
319
320    /**
321     * Update Track Length percentage if input text box holds a positive integer
322     * between 1 and 100.
323     * 
324     * @return true if trackLengthPercent was updated
325     */
326
327    public boolean setLengthPercent() {
328        boolean setOk = false;
329        try {
330            int newWidth = Integer.valueOf(lengthField.getText().trim());
331            if ((newWidth > 0) && (newWidth <= 100)) {
332                trackLengthPercent = newWidth;
333                setOk = true;
334            } 
335        } catch (NumberFormatException nfe) {
336            // do nothing, will return correct result code
337        }
338        return setOk;
339    }
340
341    void updateTrackSelect() {
342        trackStart = trackPos - selectWidth;
343        if (trackStart < 0)
344            trackStart = 0;
345
346        trackStop = trackPos + selectWidth;
347        if (trackStop >= trackLen)
348            trackStop = trackLen - 1;
349
350        try {
351            Gridded2DSet trck = new Gridded2DSet(RealTupleType.SpatialEarth2DTuple, new float[][] {
352                    java.util.Arrays.copyOfRange(trackLocs[0], trackStart, trackStop),
353                    java.util.Arrays.copyOfRange(trackLocs[1], trackStart, trackStop) },
354                    (trackStop - trackStart));
355            trackSelectDsp.setData(trck);
356        } catch (Exception exc) {
357            exc.printStackTrace();
358        }
359    }
360
361    /**
362     * Apply new settings
363     */
364
365    public void applyToDataSelection(DataSelection dataSelection) {
366        setTrackStride();
367        setVerticalStride();
368        setLengthPercent();
369
370        HashMap subset = (HashMap) ((HashMap) defaultSubset).clone();
371        double[] coords = (double[]) subset.get(ProfileAlongTrack.vertDim_name);
372
373        subset.put(ProfileAlongTrack.trackDim_name, new double[] { trackStart, trackStop,
374                trackStride });
375        subset.put(ProfileAlongTrack.vertDim_name, new double[] { coords[0], coords[1],
376                verticalStride });
377
378        MultiDimensionSubset select = new MultiDimensionSubset(subset);
379
380        Hashtable table = dataChoice.getProperties();
381        table.put(MultiDimensionSubset.key, select);
382
383        table = dataSelection.getProperties();
384        table.put(MultiDimensionSubset.key, select);
385
386        dataChoice.setDataSelection(dataSelection);
387
388    }
389
390}