Java tutorial
/* * This file is part of McIDAS-V * * Copyright 2007-2019 * Space Science and Engineering Center (SSEC) * University of Wisconsin - Madison * 1225 W. Dayton Street, Madison, WI 53706, USA * http://www.ssec.wisc.edu/mcidas * * All Rights Reserved * * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and * some McIDAS-V source code is based on IDV and VisAD source code. * * McIDAS-V is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * McIDAS-V is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser Public License for more details. * * You should have received a copy of the GNU Lesser Public License * along with this program. If not, see http://www.gnu.org/licenses. */ package ucar.unidata.idv.control.chart; import edu.wisc.ssec.mcidasv.McIDASV; import org.jfree.chart.*; import org.jfree.chart.axis.*; import org.jfree.chart.event.*; import org.jfree.chart.plot.*; import org.jfree.chart.renderer.*; import org.jfree.chart.renderer.xy.*; import org.jfree.data.*; import org.jfree.data.time.*; import org.jfree.data.xy.*; import org.jfree.ui.*; import org.python.core.*; import org.python.util.*; import ucar.unidata.data.DataAlias; import ucar.unidata.data.DataCategory; import ucar.unidata.data.DataChoice; import ucar.unidata.data.DataInstance; import ucar.unidata.data.grid.GridUtil; import ucar.unidata.data.point.*; import ucar.unidata.data.sounding.TrackDataSource; import ucar.unidata.gis.SpatialGrid; import ucar.unidata.idv.control.DisplayControlImpl; import ucar.unidata.idv.control.multi.*; import ucar.unidata.ui.TableSorter; import ucar.unidata.ui.symbol.*; import ucar.unidata.ui.symbol.StationModelManager; import ucar.unidata.util.FileManager; import ucar.unidata.util.GuiUtils; import ucar.unidata.util.LogUtil; import ucar.unidata.util.Misc; import ucar.unidata.util.ObjectListener; import ucar.unidata.util.StringUtil; import ucar.unidata.util.TwoFacedObject; import ucar.unidata.view.geoloc.NavigatedDisplay; import ucar.unidata.xml.XmlObjectStore; import ucar.visad.Util; import ucar.visad.display.Animation; import ucar.visad.display.AnimationInfo; import ucar.visad.display.AnimationWidget; import visad.*; import visad.georef.EarthLocation; import java.awt.*; import java.awt.event.*; import java.awt.geom.Rectangle2D; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.rmi.RemoteException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Comparator; import java.util.Date; import java.util.GregorianCalendar; import java.util.Hashtable; import java.util.List; import java.util.TimeZone; import java.util.Vector; import javax.swing.*; import javax.swing.event.*; import javax.swing.table.*; //import com.lavantech.gui.comp.*; /** * Abstract class for chart implementations * * * @author IDV Development Team * @version $Revision: 1.46 $ */ public abstract class ChartWrapper extends DisplayComponent implements KeyListener { /** Property change id */ public static final String PROP_TIMERANGE = "prop.timerange"; /** Property change id */ public static final String PROP_SELECTEDTIME = "prop.selectedtime"; /** The current min date to use */ private double minDate = Double.NEGATIVE_INFINITY; /** The current max date to use */ private double maxDate = Double.POSITIVE_INFINITY; /** The min date in the data */ private double dataMinDate = Double.NEGATIVE_INFINITY; /** The max date in the data */ private double dataMaxDate = Double.POSITIVE_INFINITY; /** The widget */ protected AnimationWidget animationWidget; /** List of data choices */ protected List dataChoiceWrappers; /** Resolution property widget */ private JSlider resolutionSlider; /** * The resolution. Used by derived classes to subsample the data. * Is from 0.0-1.0 and represents a percentage. */ private double resolution = 1.0; /** Used in properties to track if the user changed the order in the jlist */ private boolean chartOrderChanged; /** * This is the time series that we get the time segments from for subsetting */ protected TimeSeriesChartWrapper timeFilterSource; /** * This is the source for time selects */ protected ChartWrapper timeSelectSource; /** show animation time in charts */ private boolean showTime = false; /** map chart tim to animation time */ private boolean driveTime = false; /** This holds the entries for each wrapper in the properties gui */ private JComponent wrapperPanel; /** This holds the wrapper proeprties objects */ private List fieldProperties; /** Jython for those charts that can have jython applied to them */ private String jython = ""; /** The interpreter to use for whatever */ private PythonInterpreter interpreter; /** For properties */ private JTextField jythonFld; /** What group are we sharing animation times as */ private String animationShareGroup; /** * Default ctor */ public ChartWrapper() { } /** * Ctor * * @param name The name * @param dataChoices List of data choices */ public ChartWrapper(String name, List dataChoices) { super(name); this.dataChoiceWrappers = wrapDataChoices(dataChoices); } /** * Get the data categories for selecting data choices * * @return List of data categories */ public List getCategories() { List cats = DataCategory.parseCategories("trace", false); return Misc.newList(cats); } /** * Initialize * * @param displayControl The display control we're in. * @param dataChoices List of data choices */ public void init(MultiDisplayHolder displayControl, List dataChoices) { this.dataChoiceWrappers = wrapDataChoices(dataChoices); this.displayControl = displayControl; if (getName() == null) { setName(getTypeName()); } } /** * Called after this chart has been created. */ public void initDone() { super.initDone(); if (canDoDriveTime()) { try { AnimationInfo animationInfo = new AnimationInfo(); animationWidget = new AnimationWidget(animationInfo) { protected void handleSharedTime(Real time) { if (!hasBeenInitialized) { return; } super.handleSharedTime(time); if (!time.isMissing()) { animationTimeChanged(time); } } public String toString() { return "chart anim"; } }; if (animationShareGroup != null) { animationWidget.setShareGroup(animationShareGroup); } //Just call this here to initialize the widget animationWidget.getContents(); Animation animation = new Animation(); animationWidget.setAnimation(animation); } catch (Exception exc) { LogUtil.logException("Creating animation widget", exc); } } } /** * Utility to extract a FlatField from the data * * @param data The data * * @return The flat field * * @throws RemoteException On badness * @throws VisADException On badness */ protected FlatField getFlatField(FieldImpl data) throws VisADException, RemoteException { FlatField ff = null; if (GridUtil.isSequence(data)) { ff = (FlatField) data.getSample(0); } else { ff = (FlatField) data; } return ff; } /** * Overwritten by derived classes to return the name of the type of this chart. * ex: Histogram, Time Series, etc. * * @return The type name */ public abstract String getTypeName(); /** * Noop * * @param e The event */ public void keyPressed(KeyEvent e) { } /** * Noop * * @param e The event */ public void keyReleased(KeyEvent e) { } /** * Noop * * @param e The event */ public void keyTyped(KeyEvent e) { } /** * A utility that takes a list of DataChoice-s and * wraps each one in the DataChoiceWrapper. * We use the DataChoiceWrapper so that sub classes * can have their own extra info for each data choice. * ex: colors, etc. * * @param choices List of data choices * * @return List of data choice wrappers */ protected List wrapDataChoices(List choices) { List result = new ArrayList(); if (choices == null) { return result; } for (int i = 0; i < choices.size(); i++) { DataChoice dataChoice = (DataChoice) choices.get(i); result.add(createDataChoiceWrapper(dataChoice)); } return result; } /** * A utility to create a data choice wrapper * * @param dataChoice The data choice * * @return The data choice wrapper */ protected DataChoiceWrapper createDataChoiceWrapper(DataChoice dataChoice) { return new DataChoiceWrapper(dataChoice); } /** * Get get list of Ranges for time subsetting. If there are none * then return null. * * @return List of time ranges or null */ protected List getTimeFilterRanges() { List ranges = null; if ((minDate != Double.NEGATIVE_INFINITY) || (maxDate != Double.POSITIVE_INFINITY)) { if ((minDate != dataMinDate) || (maxDate != dataMaxDate)) { ranges = Misc.newList(new ucar.unidata.util.Range(minDate, maxDate)); } } if (timeFilterSource != null) { List filterRanges = timeFilterSource.getTimeRanges(); if (filterRanges != null) { if (ranges == null) { ranges = filterRanges; } else { ranges.addAll(filterRanges); } } } return ranges; } /** * Get the time ranges to use * * @return null */ public List getTimeRanges() { return null; } /** * Add the default menu items * * * @param items List of menu items * * @return The items list */ protected List getPopupMenuItems(List items) { if (canDoParameters()) { items.add(GuiUtils.makeMenuItem("Add Field...", this, "addField")); } if (canDoJython() && (jython != null) && (jython.trim().length() > 0)) { items.add(GuiUtils.makeMenuItem("Apply Jython", this, "applyJython", jython)); } items.add(GuiUtils.makeMenuItem("Remove Chart", this, "removeDisplayComponent")); items.add(GuiUtils.MENU_SEPARATOR); items.add(GuiUtils.makeMenuItem("Save Image...", this, "doSaveImage")); items.add(GuiUtils.makeMenuItem("Save Movie...", this, "doSaveMovie")); if (canDoDriveTime()) { items.add(GuiUtils.MENU_SEPARATOR); final JCheckBoxMenuItem mi1 = new JCheckBoxMenuItem("Show Animation Times", showTime); items.add(mi1); mi1.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { showTime = mi1.isSelected(); } }); final JCheckBoxMenuItem mi2 = new JCheckBoxMenuItem("Drive Animation Times", driveTime); items.add(mi2); mi2.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { driveTime = mi2.isSelected(); } }); } if (canDoTimeFilters() && (getDisplayGroup() != null)) { List comps = getDisplayGroup().getAncestorGroup().findDisplayComponents(ChartWrapper.class); JMenu filterMenu = null; boolean didSep = false; if (timeFilterSource != null) { filterMenu = new JMenu("Time Subset"); didSep = true; items.add(GuiUtils.MENU_SEPARATOR); items.add(filterMenu); filterMenu.add(GuiUtils.makeMenuItem("Remove", ChartWrapper.this, "removeTimeFilterSource")); } for (int i = 0; i < comps.size(); i++) { ChartWrapper chartWrapper = (ChartWrapper) comps.get(i); if (chartWrapper.getTimeRanges() == null) { continue; } if (filterMenu == null) { filterMenu = new JMenu("Time Subset"); if (!didSep) { items.add(GuiUtils.MENU_SEPARATOR); } didSep = true; items.add(filterMenu); } filterMenu.add(GuiUtils.makeMenuItem("From: " + chartWrapper.getName(), ChartWrapper.this, "setTimeFilterSource", chartWrapper)); } } if (canDoTimeSelect() && (getDisplayGroup() != null)) { List comps = getDisplayGroup().getAncestorGroup().findDisplayComponents(ChartWrapper.class); JMenu filterMenu = null; boolean didSep = false; if (timeSelectSource != null) { filterMenu = new JMenu("Time Select"); didSep = true; items.add(GuiUtils.MENU_SEPARATOR); items.add(filterMenu); filterMenu.add(GuiUtils.makeMenuItem("Remove", ChartWrapper.this, "removeTimeSelectSource")); } for (int i = 0; i < comps.size(); i++) { ChartWrapper chartWrapper = (ChartWrapper) comps.get(i); if ((chartWrapper == this) || !chartWrapper.canBeASourceForTimeSelectionEvents()) { continue; } if (filterMenu == null) { filterMenu = new JMenu("Time Select"); if (!didSep) { items.add(GuiUtils.MENU_SEPARATOR); } didSep = true; items.add(filterMenu); } filterMenu.add(GuiUtils.makeMenuItem("From: " + chartWrapper.getName(), ChartWrapper.this, "setTimeSelectSource", chartWrapper)); } } items.add(GuiUtils.MENU_SEPARATOR); items.add(GuiUtils.makeMenuItem("Properties...", this, "showProperties")); return items; } /** * Create, if needed, and return the interpreter * * @return The interpreter */ protected PythonInterpreter getInterpreter() { if (interpreter == null) { interpreter = getDisplayControl().getControlContext().getIdv().getJythonManager().createInterpreter(); } return interpreter; } /** * Get the jython text from the text field and apply it */ public void applyJython() { jython = jythonFld.getText(); applyJython(jython); } /** * Evaluate the jython. This is used so end users can write some jython to extract * and muck with the data in a chart. * * @param jython The jython */ protected void applyJython(String jython) { initializeJython(getInterpreter()); try { interpreter.eval(jython); } catch (Exception exc) { LogUtil.logException("Error evaluating Jython", exc); } } /** * Add the state of this chart to the interpreter * * @param interpreter The interpreter to initialize */ protected void initializeJython(PythonInterpreter interpreter) { interpreter.set("chart", this); } /** * Should the jython field be shown in the properties * * @return Can this chart have jython applied to it */ protected boolean canDoJython() { return false; } /** * Can this component be a source for time selection events * * @return false */ protected boolean canBeASourceForTimeSelectionEvents() { return false; } /** * Can this chart use time subset filters. * This is used to determine whether the checkbox should be shown * in the menus * * @return Can this chart use time subset filters. */ protected boolean canDoTimeFilters() { return true; } /** * Can this chart use time selects * This is used to determine whether the checkbox should be shown * in the menus * * @return Can this chart use time select */ protected boolean canDoTimeSelect() { return false; } /** * Can this chart drive the times in the main display. * This is used to determine whether the checkbox should be shown * in the menus * * @return Can this chart drive the times in the main display */ protected boolean canDoDriveTime() { return false; } /** * Can this chart subset the entire data set on time * * @return Can do min max data subsetting */ protected boolean canDoMinMaxDate() { return true; } /** * Should show resolution widget * * @return Should show resolution widget */ protected boolean canDoResolution() { return true; } /** * Can we add fields * * @return ok to add fields */ public boolean canDoParameters() { return true; } /** * Can we remove fields * * @return ok to add fields */ public boolean canDoRemoveParameters() { return true; } /** * ok to show the data choice list * * @return ok to show the data choice list */ public boolean canDoDataChoiceList() { return true; } /** * Can the color swatch be shown in the properties for the data choice wrappers. * * @return Can do wrapper colors */ public boolean canDoWrapperColor() { return false; } /** * Can the Side menu be shown in the properties for the data choice wrappers. * * @return Can do sides in properties */ public boolean canDoWrapperSide() { return false; } /** * Handle the event * * @param event The event */ public void propertyChange(PropertyChangeEvent event) { if (event.getPropertyName().equals(PROP_TIMERANGE)) { try { loadData(); } catch (Exception exc) { LogUtil.logException("Error creating data set", exc); } return; } else if (event.getPropertyName().equals(PROP_REMOVED)) { Object source = event.getSource(); if (source == timeFilterSource) { setTimeFilterSource(null); } if (source == timeSelectSource) { setTimeSelectSource(null); } } super.propertyChange(event); } /** * Cleanup the chart */ public void doRemove() { super.doRemove(); if (interpreter != null) { getDisplayControl().getControlContext().getIdv().getJythonManager().removeInterpreter(interpreter); interpreter = null; } if (timeFilterSource != null) { timeFilterSource.removePropertyChangeListener(this); timeFilterSource = null; } } /* private boolean minOk(double min) { return min != Double.NEGATIVE_INFINITY; } private boolean maxOk(double max) { return max != Double.NEGATIVE_INFINITY; } private GregorianCalendar getMinCalendar() { GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT")); if (minOk(minDate)) { cal.setTime(new Date((long) minDate)); } else if (minOk(dataMinDate)) { cal.setTime(new Date((long) dataMinDate)); } else { cal = null; } return cal; } private GregorianCalendar getMaxCalendar() { GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT")); if (minOk(minDate)) { cal.setTime(new Date((long) minDate)); } else if (minOk(dataMaxDate)) { cal.setTime(new Date((long) dataMaxDate)); } else { cal = null; } return cal; } private GregorianCalendar getCalendar(Date d) { GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT")); cal.setTime(d); return cal; } private void dateTimeChanged() { // maxPicker.setMinSelectableTime(getCalendar(minPicker.getDate())); // minPicker.setMaxSelectableTime(getCalendar(maxPicker.getDate())); } // private DateTimePicker minPicker; // private DateTimePicker maxPicker; */ /** * Create the properties contents * * @param comps List of components * @param tabIdx Which tab */ protected void getPropertiesComponents(List comps, int tabIdx) { super.getPropertiesComponents(comps, tabIdx); if (tabIdx != 0) { return; } if (canDoResolution()) { comps.add(GuiUtils.rLabel("Resolution: ")); resolutionSlider = new JSlider(0, 100, (int) (100 * resolution)); comps.add(GuiUtils.vbox(resolutionSlider, GuiUtils.leftRight(GuiUtils.lLabel("Low"), GuiUtils.rLabel("High")))); } /* if(canDoMinMaxDate() && minOk(dataMinDate) && maxOk(dataMaxDate)) { LocaleSpecificResources.setHourFormat(LocaleSpecificResources.HOUR_FORMAT_24); GregorianCalendar minCal = getMinCalendar(); GregorianCalendar maxCal = getMaxCalendar(); ActionListener listener = new ActionListener(){ public void actionPerformed(ActionEvent e) { dateTimeChanged(); } }; minPicker = new DateTimePicker(minCal, "dd-MMM-yyyy HH:mm:ss z"); minPicker.setDisplayTodayButton(false); minPicker.addActionListener(listener); minPicker.setMaxSelectableTime(maxCal); minPicker.setMinSelectableTime(getCalendar(new Date((long)dataMinDate))); maxPicker = new DateTimePicker(maxCal, "dd-MMM-yyyy HH:mm:ss z"); maxPicker.setDisplayTodayButton(false); maxPicker.addActionListener(listener); maxPicker.setMinSelectableTime(minCal); maxPicker.setMaxSelectableTime(getCalendar(new Date((long)dataMaxDate))); JPanel datePanel = GuiUtils.hbox(minPicker, new JLabel(" to "), maxPicker); comps.add(GuiUtils.rLabel("Date Range: ")); comps.add(GuiUtils.left(datePanel)); }*/ if (canDoJython()) { comps.add(GuiUtils.rLabel("Jython: ")); jythonFld = new JTextField(jython, 40); comps.add(GuiUtils.centerRight(jythonFld, GuiUtils.makeButton("Apply", this, "applyJython"))); } if (canDoDataChoiceList()) { chartOrderChanged = false; fieldProperties = new ArrayList(); for (int paramIdx = 0; paramIdx < dataChoiceWrappers.size(); paramIdx++) { DataChoiceWrapper wrapper = (DataChoiceWrapper) dataChoiceWrappers.get(paramIdx); XmlObjectStore store; if (getDisplayControl() != null) { store = getDisplayControl().getControlContext().getIdv().getStore(); } else { store = McIDASV.getStaticMcv().getStore(); } fieldProperties.add(new FieldProperties(store, wrapper)); } wrapperPanel = new JPanel(); updateWrapperPanel(); JScrollPane sp = new JScrollPane(GuiUtils.left(wrapperPanel), ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); sp.getVerticalScrollBar().setUnitIncrement(10); JViewport vp = sp.getViewport(); sp.setPreferredSize(new Dimension(600, 200)); comps.add(GuiUtils.top(GuiUtils.rLabel("Fields:"))); comps.add(sp); } } /** * Move the field in the properties list * * @param idx Which one * @param down up or down */ private void moveFieldInProperties(int idx, boolean down) { if (down && (idx == fieldProperties.size() - 1)) { return; } if (!down && (idx == 0)) { return; } Object o2 = fieldProperties.remove(idx); int newIdx = (down ? idx + 1 : idx - 1); fieldProperties.add(newIdx, o2); updateWrapperPanel(); } /** * Class FieldProperties holds state for the data choice wrappers in the properties dialog * * * @author IDV Development Team * @version $Revision: 1.46 $ */ protected static class FieldProperties { /** state */ DataChoiceWrapper wrapper; /** state */ JCheckBox removeCbx; /** state */ JComboBox sideCbx; /** state */ JTextField nameFld; /** state */ JComponent displayComp; /** * ctor * * @param wrapper The wrapper we represent */ public FieldProperties(XmlObjectStore store, DataChoiceWrapper wrapper) { this.wrapper = wrapper; nameFld = new JTextField(wrapper.getDescription(), 15); displayComp = this.wrapper.getLineState().getPropertyContents(store); this.removeCbx = new JCheckBox(" ", false); this.sideCbx = GuiUtils.makeComboBox(DataChoiceWrapper.SIDES, DataChoiceWrapper.SIDELABELS, wrapper.getSide()); } /** * apply the properties * * @return success */ public boolean applyProperties() { if (!wrapper.getLineState().applyProperties()) { return false; } wrapper.setMyDescription(nameFld.getText().trim()); wrapper.setSide(GuiUtils.getValueFromBox(sideCbx)); return true; } } /** * reload the wrapper panel in the properties gui */ private void updateWrapperPanel() { List wrapperComps = new ArrayList(); wrapperComps.add(new JLabel(" ")); wrapperComps.add(new JLabel("Remove ")); wrapperComps.add(new JLabel("Field ")); int columns = 3; if (canDoWrapperColor()) { wrapperComps.add(GuiUtils.cLabel("Width/Color/Style")); columns++; } if (canDoWrapperSide()) { wrapperComps.add(GuiUtils.cLabel("Side")); columns++; } for (int i = 0; i < fieldProperties.size(); i++) { FieldProperties fieldProperty = (FieldProperties) fieldProperties.get(i); JButton upButton = GuiUtils.getImageButton("/auxdata/ui/icons/Up16.gif", getClass()); JButton downButton = GuiUtils.getImageButton("/auxdata/ui/icons/Down16.gif", getClass()); downButton.addActionListener(new ObjectListener(new Integer(i)) { public void actionPerformed(ActionEvent ae) { moveFieldInProperties(((Integer) theObject).intValue(), true); } }); upButton.addActionListener(new ObjectListener(new Integer(i)) { public void actionPerformed(ActionEvent ae) { moveFieldInProperties(((Integer) theObject).intValue(), false); } }); wrapperComps.add(GuiUtils.hbox(upButton, downButton)); if (canDoRemoveParameters()) { wrapperComps.add(fieldProperty.removeCbx); } wrapperComps.add(GuiUtils.wrap(fieldProperty.nameFld)); if (canDoWrapperColor()) { wrapperComps.add(doMakeWrapperDisplayComponent(i, fieldProperty)); } if (canDoWrapperSide()) { wrapperComps.add(GuiUtils.wrap(fieldProperty.sideCbx)); } } wrapperPanel.removeAll(); wrapperPanel.setLayout(new BorderLayout()); GuiUtils.tmpInsets = new Insets(0, 5, 0, 5); JPanel guts = GuiUtils.doLayout(wrapperComps, columns, GuiUtils.WT_NNN, GuiUtils.WT_N); wrapperPanel.add(BorderLayout.NORTH, guts); wrapperPanel.validate(); wrapperPanel.repaint(); } /** * Make the widget for the field * * @param idx which one * @param fieldProperty The wrapper wrapper * * @return The gui */ protected JComponent doMakeWrapperDisplayComponent(int idx, FieldProperties fieldProperty) { return GuiUtils.inset(fieldProperty.displayComp, 4); } /** * Apply properties * * * @return Was successful */ protected boolean applyProperties() { if (!super.applyProperties()) { return false; } if (resolutionSlider != null) { double newResolution = resolutionSlider.getValue() / 100.0; if (newResolution != resolution) { resolution = newResolution; } } /* if(maxPicker!=null) { minDate = minPicker.getDate().getTime(); maxDate = maxPicker.getDate().getTime(); }*/ if (canDoDataChoiceList()) { dataChoiceWrappers = new ArrayList(); List tmpFields = fieldProperties; fieldProperties = new ArrayList(); for (int i = 0; i < tmpFields.size(); i++) { FieldProperties fieldProperty = (FieldProperties) tmpFields.get(i); if (!fieldProperty.applyProperties()) { return false; } } for (int i = 0; i < tmpFields.size(); i++) { FieldProperties fieldProperty = (FieldProperties) tmpFields.get(i); if (fieldProperty.removeCbx.isSelected()) { continue; } dataChoiceWrappers.add(fieldProperty.wrapper); fieldProperties.add(fieldProperty); } updateWrapperPanel(); } if (jythonFld != null) { jython = jythonFld.getText(); } return true; } /** * Apply the properties * * * @return success */ protected boolean doApplyProperties() { if (!super.doApplyProperties()) { return false; } try { loadData(); } catch (Exception exc) { LogUtil.logException("Error loading data", exc); return false; } return true; } /** * Add a field to thei chart */ public void addField() { getDisplayControl().addFieldToChartWrapper(this); } /** * Returns the list of labels used for selecting data choices. * If a derived class needs more than one data choice they * can override this. * * @return List of field labels */ public List getFieldSelectionLabels() { return Misc.newList("Choose Fields for Chart: "); } /** * When selecting data does the data tree support multiple selections * * @return Do multiples */ public boolean doMultipleAddFields() { return true; } /** * utility to format the value * * @param v the value * * @return the value formatted */ public String formatValue(double v) { return getDisplayControl().formatValue(v); } /** * Add the dta choice * * @param dataChoice the choice * * @throws RemoteException On badness * @throws VisADException On badness */ public void addDataChoice(DataChoice dataChoice) throws VisADException, RemoteException { dataChoiceWrappers.add(createDataChoiceWrapper(dataChoice)); loadData(); } /** * Add the data choices * * @param newDataChoices the choices * * @throws RemoteException On badness * @throws VisADException On badness */ public void addDataChoices(List newDataChoices) throws VisADException, RemoteException { dataChoiceWrappers.addAll(wrapDataChoices(newDataChoices)); loadData(); } /** * create shapes for an individual time step. * * @throws RemoteException On badness * @throws VisADException On badness */ public void loadData() throws VisADException, RemoteException { } /** * Animation in main display changed. Some charts show this * * @param time the animation time */ public void setTimeFromAnimation(Real time) { } /** * Use the animation time to set the domain crosshairs * * @param time The time */ public void animationTimeChanged(Real time) { super.animationTimeChanged(time); if (showTime) { setTimeFromAnimation(time); } } /** * Set the DataChoiceWrappers property. * * @param value The new value for DataChoiceWrappers */ public void setDataChoiceWrappers(List value) { dataChoiceWrappers = value; } /** * Get the DataChoiceWrappers property. * * @return The DataChoiceWrappers */ public List getDataChoiceWrappers() { return dataChoiceWrappers; } /** * Set the Resolution property. * * @param value The new value for Resolution */ public void setResolution(double value) { resolution = value; } /** * Get the Resolution property. * * @return The Resolution */ public double getResolution() { return resolution; } /** * Create and return a list of the data choices held by the data choice wrappers. * * @return List of data choices */ public List getDataChoices() { List result = new ArrayList(); for (int i = 0; i < dataChoiceWrappers.size(); i++) { result.add(((DataChoiceWrapper) dataChoiceWrappers.get(i)).getDataChoice()); } return result; } /** * Remove the current time filter source */ public void removeTimeFilterSource() { setTimeFilterSource(null); } /** * Set the FilterSource property. * * @param value The new value for FilterSource */ public void setTimeFilterSource(TimeSeriesChartWrapper value) { if (timeFilterSource != null) { timeFilterSource.removePropertyChangeListener(this); } timeFilterSource = value; if (timeFilterSource != null) { timeFilterSource.addPropertyChangeListener(this); } try { if (hasBeenInitialized) { loadData(); } } catch (Exception exc) { LogUtil.logException("Error creating data set", exc); } } /** * Get the FilterSource property. * * @return The FilterSource */ public TimeSeriesChartWrapper getTimeFilterSource() { return timeFilterSource; } /** * Remove the current time select source */ public void removeTimeSelectSource() { setTimeSelectSource(null); } /** * Set the SelectSource property. * * @param value The new value for SelectSource */ public void setTimeSelectSource(ChartWrapper value) { if (timeSelectSource != null) { timeSelectSource.removePropertyChangeListener(this); } timeSelectSource = value; if (timeSelectSource != null) { timeSelectSource.addPropertyChangeListener(this); } try { if (hasBeenInitialized) { loadData(); } } catch (Exception exc) { LogUtil.logException("Error creating data set", exc); } } /** * Get the SelectSource property. * * @return The SelectSource */ public ChartWrapper getTimeSelectSource() { return timeSelectSource; } /** * Utility to pull subset the given samples based on the time filter ranges * * @param samples The data * @param timeValues The times * * @return The sampled data/time * * @throws RemoteException On badness_ * @throws VisADException On badness_ */ protected double[][] filterData(double[] samples, double[] timeValues) throws VisADException, RemoteException { if (timeValues == null) { return new double[][] { samples }; } List timeRanges = getTimeFilterRanges(); int size = samples.length; int resolutionCnt = (int) (getResolution() * size); int stride = 1; while (size / stride > resolutionCnt) { stride++; } int valueIdx = 0; double[] values = new double[size]; double[] tmpTimeValues = new double[size]; for (int i = 0; i < size; i += stride) { if (timeRanges != null) { boolean ok = false; double timeValue = timeValues[i]; for (int rangeIdx = 0; !ok && (rangeIdx < timeRanges.size()); rangeIdx++) { ucar.unidata.util.Range r = (ucar.unidata.util.Range) timeRanges.get(rangeIdx); if ((timeValue >= r.getMin()) && (timeValue <= r.getMax())) { ok = true; } } if (!ok) { continue; } } tmpTimeValues[valueIdx] = timeValues[i]; values[valueIdx++] = samples[i]; } double[] actualValues = new double[valueIdx]; double[] actualTimeValues = new double[valueIdx]; for (int i = 0; i < valueIdx; i++) { actualValues[i] = values[i]; actualTimeValues[i] = tmpTimeValues[i]; } return new double[][] { actualValues, actualTimeValues }; } /** * Convert the time from the data (in samples[1]) into an array * of milliseconds * * @param samples The data * @param data Where the data came from * * @return The times * * @throws RemoteException On badness * @throws VisADException On badness */ public double[] getTimeValues(double[][] samples, FlatField data) throws VisADException, RemoteException { if (samples.length < 2) { return null; } Unit[] units = ucar.visad.Util.getDefaultRangeUnits((FlatField) data); double[] timeValues = CommonUnit.secondsSinceTheEpoch.toThis(samples[1], units[1]); for (int i = 0; i < timeValues.length; i++) { timeValues[i] *= 1000; if ((i == 0) || (timeValues[i] < dataMinDate)) { dataMinDate = timeValues[i]; } if ((i == 0) || (timeValues[i] > dataMaxDate)) { dataMaxDate = timeValues[i]; } } return timeValues; } /** * Set the ShowTime property. * * @param value The new value for ShowTime */ public void setShowTime(boolean value) { showTime = value; } /** * Get the ShowTime property. * * @return The ShowTime */ public boolean getShowTime() { return showTime; } /** * Set the DriveTime property. * * @param value The new value for DriveTime */ public void setDriveTime(boolean value) { driveTime = value; } /** * Get the DriveTime property. * * @return The DriveTime */ public boolean getDriveTime() { return driveTime; } /** * Set the MinDate property. * * @param value The new value for MinDate */ public void setMinDate(double value) { minDate = value; } /** * Get the MinDate property. * * @return The MinDate */ public double getMinDate() { return minDate; } /** * Set the MaxDate property. * * @param value The new value for MaxDate */ public void setMaxDate(double value) { maxDate = value; } /** * Get the MaxDate property. * * @return The MaxDate */ public double getMaxDate() { return maxDate; } /** * Set the Jython property. * * @param value The new value for Jython */ public void setJython(String value) { jython = value; } /** * Get the Jython property. * * @return The Jython */ public String getJython() { return jython; } /** * Set the AnimationShareGroup property. * * @param value The new value for AnimationShareGroup */ public void setAnimationShareGroup(String value) { animationShareGroup = value; } /** * Get the AnimationShareGroup property. * * @return The AnimationShareGroup */ public String getAnimationShareGroup() { return animationShareGroup; } }