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