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