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