001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2017
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    Hashtable table = dataChoice.getProperties();
187    table.put(MultiDimensionSubset.key, subset);
188    
189
190    track = (FlatField) dataSource.getData(dataChoice, null, getDataSelection(), dataSource.getProperties());
191
192    LineDrawing trackDsp = new LineDrawing("track");
193    trackDsp.setLineWidth(2f);
194    trackDsp.setData(track.getDomainSet());
195    mainViewMaster.addDisplayable(trackDsp);
196
197    // ??? setConstantPosition(val, display real type) ??
198    locOnTrack = new SelectorPoint("marker", new EarthLocationTuple(10, 10, 0));
199//    locOnTrack.setMarker(ShapeUtility.makeShape(ShapeUtility.CROSS));
200    VisADGeometryArray[] markerShape = ShapeUtility.createShape(ShapeUtility.CROSS);
201    locOnTrack.setMarker(markerShape[0]);
202    mainViewMaster.addDisplayable(locOnTrack);
203    locOnTrack.setScale(0.1f);
204
205    trackDisplay = trackDsp;
206    return track;
207  }
208
209  private DisplayableData create3DDisplay(FlatField data) throws VisADException, RemoteException {
210    RealType imageRangeType = (RealType) ((FunctionType)data.getType()).getRange();
211    HydraRGBDisplayable imageDsp = new HydraRGBDisplayable("image", imageRangeType, (RealType) null, true, null);
212    imageDsp.setDefaultRenderer();
213    imageDsp.setData(data);
214    return imageDsp;
215  }
216
217  private void create3DMesh(FlatField track) throws VisADException, RemoteException {
218    float del_lat = 2f;
219    int n_sets = 3;
220    GriddedSet set = (GriddedSet) track.getDomainSet();
221
222    float[][] samples = set.getSamples();
223    float[][] samples3D = new float[][] {samples[0], samples[1], new float[samples[0].length]};
224
225    SampledSet[] sets = new SampledSet[n_sets];
226    Tuple[] labels = new Tuple[n_sets];
227    float alt_start = 2000;
228    float alt_inc = 5000;
229    for (int k=0; k<n_sets; k++) {
230      for (int i=0; i<samples3D[2].length; i++) {
231        samples3D[2][i] = alt_start + k*alt_inc;
232      }
233      sets[k] = new Gridded3DSet(RealTupleType.SpatialEarth3DTuple, samples3D, samples3D[2].length);
234      Tuple tup = new Tuple(new TupleType(new MathType[] {RealTupleType.SpatialEarth3DTuple, TextType.Generic}),
235            new Data[] {new RealTuple(RealTupleType.SpatialEarth3DTuple, 
236                  new double[] {samples3D[0][0], samples3D[1][0] - del_lat, samples3D[2][0]}), 
237                          new Text(TextType.Generic, Float.toString(samples3D[2][0]))});
238      labels[k] = tup;
239    }
240
241    UnionSet u_set = new UnionSet(sets);
242    LineDrawing meshDsp = new LineDrawing("mesh");
243    meshDsp.setLineWidth(2f);
244    meshDsp.setData(u_set);
245    mainViewMaster.addDisplayable(meshDsp);
246
247    TextDisplayable txtDsp = new TextDisplayable(TextType.Generic);
248    txtDsp.setData(new Tuple(labels));
249    txtDsp.setLineWidth(2f);
250    mainViewMaster.addDisplayable(txtDsp);
251
252    meshDisplay = meshDsp;
253    textDisplay = txtDsp;
254    
255    return;
256  }
257
258  private XYDisplay makeDisplay2D(final FlatField data) throws VisADException, RemoteException {
259    
260    FunctionType fncType = (FunctionType) data.getType();
261
262    RealType domainType = RealType.Generic;
263    RealType rangeType = (RealType) fncType.getRange();
264
265    final Set domainSet = data.getDomainSet();
266    int len = domainSet.getLength();
267    Integer1DSet newDomain = new Integer1DSet(len);
268    FlatField newFF = new FlatField(new FunctionType(RealType.Generic, rangeType), newDomain);
269    newFF.setSamples(data.getFloats());
270
271    XYDisplay master = new XYDisplay("2D disp", domainType, rangeType);
272
273    master.showAxisScales(true);
274    master.setAspect(2.5, 0.75);
275    double[] proj = master.getProjectionMatrix();
276    proj[0] = 0.35;
277    proj[5] = 0.35;
278    proj[10] = 0.35;
279    master.setProjectionMatrix(proj);
280
281    ScalarMap xmap = new ScalarMap(domainType, Display.XAxis);
282    ScalarMap ymap = new ScalarMap(rangeType, Display.YAxis);
283    ScalarMap txtMap = new ScalarMap(TextType.Generic, Display.Text);
284
285    LocalDisplay display = master.getDisplay();
286    display.addMap(xmap);
287    display.addMap(ymap);
288    display.addMap(txtMap);
289
290    DataReference dataRef = new DataReferenceImpl("data");
291    dataRef.setData(newFF);
292    display.addReference(dataRef);
293
294    final DataReference txtRef = new DataReferenceImpl("text");
295    display.addReference(txtRef, new ConstantMap[] {new ConstantMap(0.9, Display.YAxis)});
296
297
298    class MyDragLine extends DragLine {
299      public MyDragLine(Gridded1DSet domain, RealType domainType, RealType rangeType,
300            final float lastSelectedValue, LocalDisplay display, final String controlId,
301            final ConstantMap[] color, float[] YRANGE) throws Exception {
302        super(domain, domainType, rangeType, lastSelectedValue, display, controlId, color, YRANGE);
303      }
304
305      public void update() {
306         int idx = (new Float(this.lastSelectedValue)).intValue();
307         try {
308           float[][] val = domainSet.indexToValue(new int[] {idx});
309           locOnTrack.setPoint(new EarthLocationTuple(val[1][0], val[0][0], 0));
310           float rangeVal = (float) ((Real)data.getSample(idx)).getValue();
311           Tuple tup = new Tuple(new Data[] {new Real(RealType.Generic, (double) idx), new Text(TextType.Generic, numFmt.format(rangeVal))});
312           txtRef.setData(tup);
313           
314         } catch (Exception e) {
315           System.out.println(e);
316         }
317      }
318    }
319
320    try {
321        MyDragLine draggable = new MyDragLine(newDomain, domainType, rangeType, 100f, display, 
322            "dragLine", MultiSpectralDisplay.makeColorMap(Color.GREEN), new float[] {0, 16});
323    } catch (Exception e) {
324      e.printStackTrace();
325    }
326
327    return master;
328  }
329
330  protected ColorTable getInitialColorTable() {
331    return getDisplayConventions().getParamColorTable(imageRangeType.getName());
332  }
333
334  protected Range getInitialRange() throws RemoteException, VisADException {
335      Range range = getDisplayConventions().getParamRange(imageRangeType.getName(), null);
336      if (range != null) {
337        setSelectRange(range);
338        return range;
339      }
340      else {
341        return super.getInitialRange();
342      }
343  }
344
345  public void doRemove() throws RemoteException, VisADException{
346    
347    if (meshDisplay != null) mainViewMaster.removeDisplayable(meshDisplay);
348    if (textDisplay != null) mainViewMaster.removeDisplayable(textDisplay);
349    if (trackDisplay != null) mainViewMaster.removeDisplayable(trackDisplay);
350    super.doRemove();
351  }
352
353  public void setDisplayVisibility(boolean on) {
354    super.setDisplayVisibility(on);
355    try {
356      if (meshDisplay != null) meshDisplay.setVisible(on);
357      if (textDisplay != null) textDisplay.setVisible(on);
358      if (trackDisplay != null) trackDisplay.setVisible(on);
359    }
360    catch( Exception e) {
361      e.printStackTrace();
362    }
363  }
364
365  public Container doMakeContents() {
366        try {
367            JTabbedPane pane = new JTabbedPane();
368            if (display2D != null) {
369              pane.add("Display", GuiUtils.inset(display2D.getDisplayComponent(), 5));
370            }
371            pane.add("Settings",
372                     GuiUtils.inset(GuiUtils.top(doMakeWidgetComponent()), 5));
373            GuiUtils.handleHeavyWeightComponentsInTabs(pane);
374            return pane;
375        } catch (Exception e) {
376            logException("MultiSpectralControl.doMakeContents", e);
377        }
378        return null;
379  }
380
381  protected JComponent doMakeWidgetComponent() {
382        List<Component> widgetComponents;
383        try {
384            List<ControlWidget> controlWidgets = new ArrayList<ControlWidget>();
385            getControlWidgets(controlWidgets);
386            widgetComponents = ControlWidget.fillList(controlWidgets);
387        } catch (Exception e) {
388            LogUtil.logException("Problem building the ProfileAlongTrackControl settings", e);
389            widgetComponents = new ArrayList<Component>();
390            widgetComponents.add(new JLabel("Error building component..."));
391        }
392
393        GuiUtils.tmpInsets = new Insets(4, 8, 4, 8);
394        GuiUtils.tmpFill = GridBagConstraints.HORIZONTAL;
395        return GuiUtils.doLayout(widgetComponents, 2, GuiUtils.WT_NY, GuiUtils.WT_N);
396    }
397
398}