001/* 002 * $Id: McIDASVHistogramWrapper.java,v 1.23 2011/03/24 16:06:32 davep Exp $ 003 * 004 * This file is part of McIDAS-V 005 * 006 * Copyright 2007-2011 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 031package edu.wisc.ssec.mcidasv.control; 032 033import java.awt.Color; 034import java.awt.event.ActionEvent; 035import java.awt.event.ActionListener; 036import java.rmi.RemoteException; 037import java.util.ArrayList; 038import java.util.Hashtable; 039import java.util.List; 040 041import javax.swing.JCheckBox; 042import javax.swing.JComponent; 043import javax.swing.JLabel; 044import javax.swing.JMenu; 045import javax.swing.JMenuItem; 046import javax.swing.JTextField; 047 048import org.jfree.chart.ChartFactory; 049import org.jfree.chart.ChartPanel; 050import org.jfree.chart.axis.NumberAxis; 051import org.jfree.chart.axis.ValueAxis; 052import org.jfree.chart.event.AxisChangeEvent; 053import org.jfree.chart.event.AxisChangeListener; 054import org.jfree.chart.plot.PlotOrientation; 055import org.jfree.chart.plot.XYPlot; 056import org.jfree.chart.renderer.xy.StackedXYBarRenderer; 057import org.jfree.chart.renderer.xy.XYBarRenderer; 058import org.jfree.chart.renderer.xy.XYItemRenderer; 059import org.jfree.data.Range; 060import org.jfree.data.statistics.HistogramType; 061import org.jfree.util.Log; 062import org.slf4j.Logger; 063import org.slf4j.LoggerFactory; 064 065import visad.FlatField; 066import visad.Unit; 067import visad.VisADException; 068 069import ucar.unidata.data.DataCancelException; 070import ucar.unidata.data.DataChoice; 071import ucar.unidata.idv.DisplayControl; 072import ucar.unidata.idv.control.DisplayControlImpl; 073import ucar.unidata.idv.control.chart.ChartWrapper; 074import ucar.unidata.idv.control.chart.DataChoiceWrapper; 075import ucar.unidata.idv.control.chart.HistogramWrapper; 076import ucar.unidata.idv.control.chart.MyHistogramDataset; 077import ucar.unidata.idv.control.multi.DisplayGroup; 078import ucar.unidata.idv.ui.ImageSequenceGrabber; 079import ucar.unidata.util.GuiUtils; 080import ucar.unidata.util.LogUtil; 081import ucar.unidata.util.Misc; 082 083/** 084 * Wraps a JFreeChart histograms to ease working with VisAD data. 085 */ 086public 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