001 /*
002 * $Id: McIDASVHistogramWrapper.java,v 1.24 2012/02/19 17:35:37 davep 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.Color;
034 import java.awt.event.ActionEvent;
035 import java.awt.event.ActionListener;
036 import java.rmi.RemoteException;
037 import java.util.ArrayList;
038 import java.util.Hashtable;
039 import java.util.List;
040
041 import javax.swing.JCheckBox;
042 import javax.swing.JComponent;
043 import javax.swing.JLabel;
044 import javax.swing.JMenu;
045 import javax.swing.JMenuItem;
046 import javax.swing.JTextField;
047
048 import org.jfree.chart.ChartFactory;
049 import org.jfree.chart.ChartPanel;
050 import org.jfree.chart.axis.NumberAxis;
051 import org.jfree.chart.axis.ValueAxis;
052 import org.jfree.chart.event.AxisChangeEvent;
053 import org.jfree.chart.event.AxisChangeListener;
054 import org.jfree.chart.plot.PlotOrientation;
055 import org.jfree.chart.plot.XYPlot;
056 import org.jfree.chart.renderer.xy.StackedXYBarRenderer;
057 import org.jfree.chart.renderer.xy.XYBarRenderer;
058 import org.jfree.chart.renderer.xy.XYItemRenderer;
059 import org.jfree.data.Range;
060 import org.jfree.data.statistics.HistogramType;
061 import org.jfree.util.Log;
062 import org.slf4j.Logger;
063 import org.slf4j.LoggerFactory;
064
065 import visad.FlatField;
066 import visad.Unit;
067 import visad.VisADException;
068
069 import ucar.unidata.data.DataCancelException;
070 import ucar.unidata.data.DataChoice;
071 import ucar.unidata.idv.DisplayControl;
072 import ucar.unidata.idv.control.DisplayControlImpl;
073 import ucar.unidata.idv.control.chart.ChartWrapper;
074 import ucar.unidata.idv.control.chart.DataChoiceWrapper;
075 import ucar.unidata.idv.control.chart.HistogramWrapper;
076 import ucar.unidata.idv.control.chart.MyHistogramDataset;
077 import ucar.unidata.idv.control.multi.DisplayGroup;
078 import ucar.unidata.idv.ui.ImageSequenceGrabber;
079 import ucar.unidata.util.GuiUtils;
080 import ucar.unidata.util.LogUtil;
081 import ucar.unidata.util.Misc;
082
083 /**
084 * Wraps a JFreeChart histograms to ease working with VisAD data.
085 */
086 public class McIDASVHistogramWrapper extends HistogramWrapper {
087
088 private static final Logger logger = LoggerFactory.getLogger(McIDASVHistogramWrapper.class);
089
090 private DisplayControl imageControl;
091
092 /** The plot */
093 private XYPlot plot;
094
095 private double low;
096
097 private double high;
098
099 /**
100 * Default ctor
101 */
102 public McIDASVHistogramWrapper() {}
103
104 /**
105 * Ctor
106 *
107 * @param name The name.
108 * @param dataChoices List of data choices.
109 * @param control {@literal "Parent"} control.
110 */
111 public McIDASVHistogramWrapper(String name, List dataChoices, DisplayControlImpl control) {
112 super(name, dataChoices);
113 imageControl = control;
114 }
115
116 /**
117 * Create the chart
118 */
119 private void createChart() {
120 if (chartPanel != null) {
121 return;
122 }
123
124 MyHistogramDataset dataset = new MyHistogramDataset();
125 chart = ChartFactory.createHistogram("Histogram", null, null,
126 dataset,
127 PlotOrientation.VERTICAL, false,
128 false, false);
129 chart.getXYPlot().setForegroundAlpha(0.75f);
130 plot = (XYPlot) chart.getPlot();
131 initXYPlot(plot);
132 chartPanel = doMakeChartPanel(chart);
133 }
134
135 public JComponent doMakeContents() {
136 return super.doMakeContents();
137 }
138
139 /**
140 * Create the charts
141 *
142 * @throws RemoteException On badness
143 * @throws VisADException On badness
144 */
145 public void loadData(FlatField data) throws VisADException, RemoteException {
146 createChart();
147 List dataChoiceWrappers = getDataChoiceWrappers();
148 try {
149 for (int dataSetIdx = 0; dataSetIdx < plot.getDatasetCount(); dataSetIdx++) {
150 MyHistogramDataset dataset = (MyHistogramDataset)plot.getDataset(dataSetIdx);
151 dataset.removeAllSeries();
152 }
153
154 Hashtable props = new Hashtable();
155 for (int paramIdx = 0; paramIdx < dataChoiceWrappers.size(); paramIdx++) {
156 DataChoiceWrapper wrapper = (DataChoiceWrapper)dataChoiceWrappers.get(paramIdx);
157
158 DataChoice dataChoice = wrapper.getDataChoice();
159 props = dataChoice.getProperties();
160 Unit unit = ucar.visad.Util.getDefaultRangeUnits((FlatField) data)[0];
161 double[][] samples = data.getValues(false);
162 double[] actualValues = filterData(samples[0], getTimeValues(samples, data))[0];
163 final NumberAxis domainAxis = new NumberAxis(wrapper.getLabel(unit));
164
165 domainAxis.setAutoRangeIncludesZero(false);
166
167 XYItemRenderer renderer;
168 if (getStacked()) {
169 renderer = new StackedXYBarRenderer();
170 } else {
171 renderer = new XYBarRenderer();
172 }
173 plot.setRenderer(paramIdx, renderer);
174 Color c = wrapper.getColor(paramIdx);
175 domainAxis.setLabelPaint(c);
176 renderer.setSeriesPaint(0, c);
177
178 MyHistogramDataset dataset = new MyHistogramDataset();
179 dataset.setType(HistogramType.FREQUENCY);
180 dataset.addSeries(dataChoice.getName() + " [" + unit + "]",
181 actualValues, getBins());
182 plot.setDomainAxis(paramIdx, domainAxis, false);
183 plot.mapDatasetToDomainAxis(paramIdx, paramIdx);
184 plot.setDataset(paramIdx, dataset);
185
186 domainAxis.addChangeListener(new AxisChangeListener() {
187 public void axisChanged(AxisChangeEvent ae) {
188 if (!imageControl.isInitDone()) {
189 return;
190 }
191
192 Range range = domainAxis.getRange();
193 double newLow = Math.floor(range.getLowerBound()+0.5);
194 double newHigh = Math.floor(range.getUpperBound()+0.5);
195 double prevLow = getLow();
196 double prevHigh = getHigh();
197 try {
198 ucar.unidata.util.Range newRange;
199 if (prevLow > prevHigh) {
200 newRange = new ucar.unidata.util.Range(newHigh, newLow);
201 } else {
202 newRange = new ucar.unidata.util.Range(newLow, newHigh);
203 }
204 ((DisplayControlImpl)imageControl).setRange(newRange);
205 } catch (Exception e) {
206 System.out.println("Can't set new range e=" + e);
207 }
208 ValueAxis rangeAxis = plot.getRangeAxis();
209 }
210 });
211
212 Range range = domainAxis.getRange();
213 low = range.getLowerBound();
214 high = range.getUpperBound();
215 }
216
217 } catch (Exception exc) {
218 System.out.println("Exception exc=" + exc);
219 LogUtil.logException("Error creating data set", exc);
220 return;
221 }
222 }
223
224 protected boolean modifyRange(double lowVal, double hiVal) {
225 try {
226 if (plot == null) {
227 return false;
228 }
229 ValueAxis domainAxis = plot.getDomainAxis();
230 domainAxis.setRange(lowVal, hiVal);
231 return true;
232 } catch (Exception e) {
233 return true;
234 }
235 }
236
237 protected Range getRange() {
238 ValueAxis domainAxis = plot.getDomainAxis();
239 return domainAxis.getRange();
240 }
241
242 protected void doReset() {
243 resetPlot();
244 }
245
246 /**
247 * reset the axis'
248 */
249 public void resetPlot() {
250 if (chart == null) {
251 return;
252 }
253 if (!(chart.getPlot() instanceof XYPlot)) {
254 return;
255 }
256 XYPlot plot = (XYPlot)chart.getPlot();
257 int rcnt = plot.getRangeAxisCount();
258 for (int i = 0; i < rcnt; i++) {
259 ValueAxis axis = (ValueAxis)plot.getRangeAxis(i);
260 axis.setAutoRange(true);
261 }
262 int dcnt = plot.getDomainAxisCount();
263 for (int i = 0; i < dcnt; i++) {
264 ValueAxis axis = (ValueAxis)plot.getDomainAxis(i);
265 try {
266 axis.setRange(low, high);
267 } catch (Exception e) {
268 logger.warn("jfreechart does not like ranges to be high -> low", e);
269 }
270 axis.setAutoRange(true);
271 }
272 }
273
274 public double getLow() {
275 return low;
276 }
277
278 public void setLow(double val) {
279 low = val;
280 }
281
282 public double getHigh() {
283 return high;
284 }
285
286 public void setHigh(double val) {
287 high = val;
288 }
289
290 /**
291 * SHow the popup menu
292 *
293 * @param where component to show near to
294 * @param x x
295 * @param y y
296 */
297 @Override public void showPopup(JComponent where, int x, int y) {
298 List items = new ArrayList();
299 items = getPopupMenuItems(items);
300 if (items.isEmpty()) {
301 return;
302 }
303 GuiUtils.makePopupMenu(items).show(where, x, y);
304 }
305
306 /**
307 * Add the default menu items
308 *
309 * @param items List of menu items
310 *
311 * @return The items list
312 */
313 @Override protected List getPopupMenuItems(List items) {
314 items = super.getPopupMenuItems(items);
315 for (Object o : items) {
316 if (o instanceof JMenuItem) {
317 JMenuItem menuItem = (JMenuItem)o;
318 if ("Properties...".equals(menuItem.getText())) {
319 menuItem.setActionCommand(ChartPanel.PROPERTIES_COMMAND);
320 menuItem.addActionListener(buildHistoPropsListener());
321 }
322 }
323 }
324 return items;
325 }
326
327 /**
328 *
329 * @return
330 */
331 private ActionListener buildHistoPropsListener() {
332 return new ActionListener() {
333 @Override public void actionPerformed(ActionEvent event) {
334 String command = event.getActionCommand();
335 if (ChartPanel.PROPERTIES_COMMAND.equals(command)) {
336 McIDASVHistogramWrapper.this.showProperties();
337 return;
338 }
339 }
340 };
341 }
342
343 /**
344 * Show the properties dialog
345 *
346 * @return Was it ok
347 */
348 @Override public boolean showProperties() {
349 boolean result;
350 if (!hasDisplayControl()) {
351 result = showProperties(null, 0, 0);
352 } else {
353 result = super.showProperties();
354 }
355 return result;
356 }
357
358 public boolean hasDisplayControl() {
359 return getDisplayControl() != null;
360 }
361
362 /**
363 * Remove me
364 *
365 * @return was removed
366 */
367 public boolean removeDisplayComponent() {
368 if (GuiUtils.askYesNo("Remove Display",
369 "Are you sure you want to remove: "
370 + toString())) {
371 DisplayGroup displayGroup = getDisplayGroup();
372 if (displayGroup != null) {
373 displayGroup.removeDisplayComponent(this);
374 }
375
376 if (hasDisplayControl()) {
377 getDisplayControl().removeDisplayComponent(this);
378 }
379 return true;
380 } else {
381 return false;
382 }
383 }
384
385 /**
386 * Apply the properties
387 *
388 * @return Success
389 */
390 @Override protected boolean doApplyProperties() {
391 applyProperties();
392
393 // try {
394 // // need to deal with the data being an imageseq
395 // loadData((FlatField)imageControl.getDataChoice().getData(null));
396 // } catch (RemoteException e) {
397 // logger.error("trying to reload data", e);
398 // } catch (DataCancelException e) {
399 // logger.error("trying to reload data", e);
400 // } catch (VisADException e) {
401 // logger.error("trying to reload data", e);
402 // }
403
404 return true;
405 }
406
407
408
409 /**
410 * Been removed, do any cleanup
411 */
412 public void doRemove() {
413 isRemoved = true;
414 List displayables = getDisplayables();
415 if (hasDisplayControl() && !displayables.isEmpty()) {
416 getDisplayControl().removeDisplayables(displayables);
417 }
418 firePropertyChange(PROP_REMOVED, null, this);
419 }
420 }
421