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 }