001    /*
002     * This file is part of McIDAS-V
003     *
004     * Copyright 2007-2013
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    
029    package edu.wisc.ssec.mcidasv.control;
030    
031    import java.awt.Container;
032    import java.awt.Component;
033    import java.awt.Insets;
034    import java.awt.Color;
035    import java.awt.GridBagConstraints;
036    import javax.swing.JLabel;
037    import javax.swing.JComponent;
038    import javax.swing.JTabbedPane;
039    import java.rmi.RemoteException;
040    import java.util.Enumeration;
041    import java.util.HashMap;
042    import java.util.Hashtable;
043    import java.util.List;
044    import java.util.ArrayList;
045    import java.text.DecimalFormat;
046    
047    import visad.Data;
048    import visad.DataReference;
049    import visad.DataReferenceImpl;
050    import visad.FlatField;
051    import visad.Real;
052    import visad.FunctionType;
053    import visad.Integer1DSet;
054    import visad.GriddedSet;
055    import visad.Gridded1DSet;
056    import visad.Gridded3DSet;
057    import visad.MathType;
058    import visad.RealTuple;
059    import visad.RealTupleType;
060    import visad.RealType;
061    import visad.Set;
062    import visad.SampledSet;
063    import visad.SimpleSet;
064    import visad.Text;
065    import visad.TextType;
066    import visad.Tuple;
067    import visad.TupleType;
068    import visad.UnionSet;
069    import visad.ScalarMap;
070    import visad.Display;
071    import visad.LocalDisplay;
072    import visad.ConstantMap;
073    import visad.VisADException;
074    import visad.VisADGeometryArray;
075    import visad.georef.LatLonTuple;
076    import visad.georef.EarthLocationTuple;
077    import visad.util.Util;
078    
079    import ucar.unidata.idv.control.ControlWidget;
080    import ucar.unidata.data.DataChoice;
081    import ucar.unidata.data.DirectDataChoice;
082    import ucar.unidata.data.DerivedDataChoice;
083    import ucar.unidata.data.GeoSelection;
084    import ucar.unidata.data.GeoSelectionPanel;
085    import ucar.unidata.idv.IntegratedDataViewer;
086    import ucar.unidata.idv.ViewManager;
087    import ucar.unidata.idv.control.DisplayControlImpl;
088    import ucar.unidata.util.ColorTable;
089    import ucar.unidata.util.GuiUtils;
090    import ucar.unidata.util.LogUtil;
091    import ucar.unidata.util.Range;
092    import ucar.visad.display.DisplayMaster;
093    import ucar.visad.display.DisplayableData;
094    import ucar.visad.display.LineDrawing;
095    import ucar.visad.display.TextDisplayable;
096    import ucar.visad.display.XYDisplay;
097    import ucar.visad.display.SelectorPoint;
098    import ucar.visad.ShapeUtility;
099    
100    import edu.wisc.ssec.mcidasv.data.hydra.HydraRGBDisplayable;
101    import edu.wisc.ssec.mcidasv.data.hydra.MultiDimensionDataSource;
102    import edu.wisc.ssec.mcidasv.data.hydra.MultiDimensionSubset;
103    import edu.wisc.ssec.mcidasv.data.hydra.GrabLineRendererJ3D;
104    import edu.wisc.ssec.mcidasv.display.hydra.DragLine;
105    import edu.wisc.ssec.mcidasv.display.hydra.MultiSpectralDisplay;
106    
107    
108    
109    public class ProfileAlongTrackControl extends DisplayControlImpl {
110    
111      private DataChoice dataChoice;
112      
113      private DisplayableData imageDisplay;
114      private DisplayableData trackDisplay;
115      private DisplayableData meshDisplay;
116      private DisplayableData textDisplay;
117    
118      private DisplayMaster mainViewMaster;
119    
120      private RealType imageRangeType;
121    
122      public MultiDimensionSubset subset;
123    
124      private MultiDimensionDataSource dataSource;
125    
126      private FlatField track;
127    
128      private GeoSelection geoSelection;
129    
130      private GeoSelectionPanel geoSelectionPanel;
131    
132      private XYDisplay display2D = null;
133    
134      private SelectorPoint locOnTrack;
135    
136      private DecimalFormat numFmt = new DecimalFormat();
137    
138    
139      public ProfileAlongTrackControl() {
140        super();
141        setAttributeFlags(FLAG_COLORTABLE | FLAG_SELECTRANGE);
142      }
143    
144      public boolean init(DataChoice dataChoice) throws VisADException, RemoteException {
145        this.dataChoice = dataChoice;
146        FlatField data;
147    
148        if (dataChoice instanceof DerivedDataChoice) {
149          data = (FlatField) dataChoice.getData(getDataSelection());
150        }
151        else {
152          dataSource = (MultiDimensionDataSource) ((DirectDataChoice)dataChoice).getDataSource();
153          ViewManager vm = getViewManager();
154          mainViewMaster = vm.getMaster();
155    
156          Hashtable table = dataChoice.getProperties();
157          Enumeration keys = table.keys();
158          while (keys.hasMoreElements()) {
159            Object key = keys.nextElement();
160            if (key instanceof MultiDimensionSubset) {
161               subset = (MultiDimensionSubset) table.get(key);
162            }
163          }
164          subset.setGeoSelection(getDataSelection().getGeoSelection());
165    
166          data = (FlatField) dataSource.getData(dataChoice, null, getDataSelection(), dataSource.getProperties());
167        }
168    
169        if (data == null) {
170          return false;
171        }
172    
173        imageRangeType = (RealType) ((FunctionType)data.getType()).getRange();
174        track = createTrackDisplay(dataChoice);
175        imageDisplay = create3DDisplay(data);
176        addDisplayable(imageDisplay, FLAG_COLORTABLE | FLAG_SELECTRANGE);
177        if (track != null) create3DMesh(track);
178    
179        // 2D Display in Control Window, only line graph type display for now
180        if (((SimpleSet)data.getDomainSet()).getManifoldDimension() == 1) {
181          display2D = makeDisplay2D(data);
182        }
183    
184        return true;
185      }
186    
187      public synchronized void dataChanged() {
188        super.dataChanged();
189      }
190    
191      private FlatField createTrackDisplay(DataChoice dataChoice) throws VisADException, RemoteException {
192        IntegratedDataViewer idv = getIdv();
193        FlatField track = null;
194    
195        dataChoice = dataSource.findDataChoice("Track3D");
196        if (dataChoice == null) {
197           return null;
198        }
199    
200        track = (FlatField) dataSource.getData(dataSource.findDataChoice("Track3D"), null, getDataSelection(), dataSource.getProperties());
201    
202        LineDrawing trackDsp = new LineDrawing("track");
203        trackDsp.setLineWidth(2f);
204        trackDsp.setData(track.getDomainSet());
205        mainViewMaster.addDisplayable(trackDsp);
206    
207        // ??? setConstantPosition(val, display real type) ??
208        locOnTrack = new SelectorPoint("marker", new EarthLocationTuple(10, 10, 0));
209    //    locOnTrack.setMarker(ShapeUtility.makeShape(ShapeUtility.CROSS));
210        VisADGeometryArray[] markerShape = ShapeUtility.createShape(ShapeUtility.CROSS);
211        locOnTrack.setMarker(markerShape[0]);
212        mainViewMaster.addDisplayable(locOnTrack);
213        locOnTrack.setScale(0.1f);
214    
215        trackDisplay = trackDsp;
216        return track;
217      }
218    
219      private DisplayableData create3DDisplay(FlatField data) throws VisADException, RemoteException {
220        RealType imageRangeType = (RealType) ((FunctionType)data.getType()).getRange();
221        HydraRGBDisplayable imageDsp = new HydraRGBDisplayable("image", imageRangeType, (RealType) null, true, null);
222        imageDsp.setDefaultRenderer();
223        imageDsp.setData(data);
224        return imageDsp;
225      }
226    
227      private void create3DMesh(FlatField track) throws VisADException, RemoteException {
228        float del_lat = 2f;
229        int n_sets = 3;
230        GriddedSet set = (GriddedSet) track.getDomainSet();
231    
232        float[][] samples = set.getSamples();
233        float[][] samples3D = new float[][] {samples[0], samples[1], new float[samples[0].length]};
234    
235        SampledSet[] sets = new SampledSet[n_sets];
236        Tuple[] labels = new Tuple[n_sets];
237        float alt_start = 2000;
238        float alt_inc = 5000;
239        for (int k=0; k<n_sets; k++) {
240          for (int i=0; i<samples3D[2].length; i++) {
241            samples3D[2][i] = alt_start + k*alt_inc;
242          }
243          sets[k] = new Gridded3DSet(RealTupleType.SpatialEarth3DTuple, samples3D, samples3D[2].length);
244          Tuple tup = new Tuple(new TupleType(new MathType[] {RealTupleType.SpatialEarth3DTuple, TextType.Generic}),
245                new Data[] {new RealTuple(RealTupleType.SpatialEarth3DTuple, 
246                      new double[] {samples3D[0][0], samples3D[1][0] - del_lat, samples3D[2][0]}), 
247                              new Text(TextType.Generic, Float.toString(samples3D[2][0]))});
248          labels[k] = tup;
249        }
250    
251        UnionSet u_set = new UnionSet(sets);
252        LineDrawing meshDsp = new LineDrawing("mesh");
253        meshDsp.setLineWidth(2f);
254        meshDsp.setData(u_set);
255        mainViewMaster.addDisplayable(meshDsp);
256    
257        TextDisplayable txtDsp = new TextDisplayable(TextType.Generic);
258        txtDsp.setData(new Tuple(labels));
259        txtDsp.setLineWidth(2f);
260        mainViewMaster.addDisplayable(txtDsp);
261    
262        meshDisplay = meshDsp;
263        textDisplay = txtDsp;
264        
265        return;
266      }
267    
268      private XYDisplay makeDisplay2D(final FlatField data) throws VisADException, RemoteException {
269        
270        FunctionType fncType = (FunctionType) data.getType();
271    
272        RealType domainType = RealType.Generic;
273        RealType rangeType = (RealType) fncType.getRange();
274    
275        final Set domainSet = data.getDomainSet();
276        int len = domainSet.getLength();
277        Integer1DSet newDomain = new Integer1DSet(len);
278        FlatField newFF = new FlatField(new FunctionType(RealType.Generic, rangeType), newDomain);
279        newFF.setSamples(data.getFloats());
280    
281        XYDisplay master = new XYDisplay("2D disp", domainType, rangeType);
282    
283        master.showAxisScales(true);
284        master.setAspect(2.5, 0.75);
285        double[] proj = master.getProjectionMatrix();
286        proj[0] = 0.35;
287        proj[5] = 0.35;
288        proj[10] = 0.35;
289        master.setProjectionMatrix(proj);
290    
291        ScalarMap xmap = new ScalarMap(domainType, Display.XAxis);
292        ScalarMap ymap = new ScalarMap(rangeType, Display.YAxis);
293        ScalarMap txtMap = new ScalarMap(TextType.Generic, Display.Text);
294    
295        LocalDisplay display = master.getDisplay();
296        display.addMap(xmap);
297        display.addMap(ymap);
298        display.addMap(txtMap);
299    
300        DataReference dataRef = new DataReferenceImpl("data");
301        dataRef.setData(newFF);
302        display.addReference(dataRef);
303    
304        final DataReference txtRef = new DataReferenceImpl("text");
305        display.addReference(txtRef, new ConstantMap[] {new ConstantMap(0.9, Display.YAxis)});
306    
307    
308        class MyDragLine extends DragLine {
309          public MyDragLine(Gridded1DSet domain, RealType domainType, RealType rangeType,
310                final float lastSelectedValue, LocalDisplay display, final String controlId,
311                final ConstantMap[] color, float[] YRANGE) throws Exception {
312            super(domain, domainType, rangeType, lastSelectedValue, display, controlId, color, YRANGE);
313          }
314    
315          public void update() {
316             int idx = (new Float(this.lastSelectedValue)).intValue();
317             try {
318               float[][] val = domainSet.indexToValue(new int[] {idx});
319               locOnTrack.setPoint(new EarthLocationTuple(val[1][0], val[0][0], 0));
320               float rangeVal = (float) ((Real)data.getSample(idx)).getValue();
321               Tuple tup = new Tuple(new Data[] {new Real(RealType.Generic, (double) idx), new Text(TextType.Generic, numFmt.format(rangeVal))});
322               txtRef.setData(tup);
323               
324             } catch (Exception e) {
325               System.out.println(e);
326             }
327          }
328        }
329    
330        try {
331            MyDragLine draggable = new MyDragLine(newDomain, domainType, rangeType, 100f, display, 
332                "dragLine", MultiSpectralDisplay.makeColorMap(Color.GREEN), new float[] {0, 16});
333        } catch (Exception e) {
334          e.printStackTrace();
335        }
336    
337        return master;
338      }
339    
340      protected ColorTable getInitialColorTable() {
341        return getDisplayConventions().getParamColorTable(imageRangeType.getName());
342      }
343    
344      protected Range getInitialRange() throws RemoteException, VisADException {
345          Range range = getDisplayConventions().getParamRange(imageRangeType.getName(), null);
346          if (range != null) {
347            setSelectRange(range);
348            return range;
349          }
350          else {
351            return super.getInitialRange();
352          }
353      }
354    
355      public void doRemove() throws RemoteException, VisADException{
356        
357        if (meshDisplay != null) mainViewMaster.removeDisplayable(meshDisplay);
358        if (textDisplay != null) mainViewMaster.removeDisplayable(textDisplay);
359        if (trackDisplay != null) mainViewMaster.removeDisplayable(trackDisplay);
360        super.doRemove();
361      }
362    
363      public void setDisplayVisibility(boolean on) {
364        super.setDisplayVisibility(on);
365        try {
366          if (meshDisplay != null) meshDisplay.setVisible(on);
367          if (textDisplay != null) textDisplay.setVisible(on);
368          if (trackDisplay != null) trackDisplay.setVisible(on);
369        }
370        catch( Exception e) {
371          e.printStackTrace();
372        }
373      }
374    
375      public Container doMakeContents() {
376            try {
377                JTabbedPane pane = new JTabbedPane();
378                if (display2D != null) {
379                  pane.add("Display", GuiUtils.inset(display2D.getDisplayComponent(), 5));
380                }
381                pane.add("Settings",
382                         GuiUtils.inset(GuiUtils.top(doMakeWidgetComponent()), 5));
383                GuiUtils.handleHeavyWeightComponentsInTabs(pane);
384                return pane;
385            } catch (Exception e) {
386                logException("MultiSpectralControl.doMakeContents", e);
387            }
388            return null;
389      }
390    
391      protected JComponent doMakeWidgetComponent() {
392            List<Component> widgetComponents;
393            try {
394                List<ControlWidget> controlWidgets = new ArrayList<ControlWidget>();
395                getControlWidgets(controlWidgets);
396                widgetComponents = ControlWidget.fillList(controlWidgets);
397            } catch (Exception e) {
398                LogUtil.logException("Problem building the ProfileAlongTrackControl settings", e);
399                widgetComponents = new ArrayList<Component>();
400                widgetComponents.add(new JLabel("Error building component..."));
401            }
402    
403            GuiUtils.tmpInsets = new Insets(4, 8, 4, 8);
404            GuiUtils.tmpFill = GridBagConstraints.HORIZONTAL;
405            return GuiUtils.doLayout(widgetComponents, 2, GuiUtils.WT_NY, GuiUtils.WT_N);
406        }
407    
408      private JComponent getDisplayTab() {
409         return null;
410      }
411    }