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