| package org.eclipse.stem.ui.reports.views; |
| |
| /******************************************************************************* |
| * Copyright (c) 2007 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.birt.chart.device.IDeviceRenderer; |
| import org.eclipse.birt.chart.exception.ChartException; |
| import org.eclipse.birt.chart.factory.Generator; |
| import org.eclipse.birt.chart.model.Chart; |
| import org.eclipse.birt.chart.model.ChartWithAxes; |
| import org.eclipse.birt.chart.model.attribute.Anchor; |
| import org.eclipse.birt.chart.model.attribute.AxisType; |
| import org.eclipse.birt.chart.model.attribute.Bounds; |
| import org.eclipse.birt.chart.model.attribute.ColorDefinition; |
| import org.eclipse.birt.chart.model.attribute.IntersectionType; |
| import org.eclipse.birt.chart.model.attribute.JavaNumberFormatSpecifier; |
| import org.eclipse.birt.chart.model.attribute.LineAttributes; |
| import org.eclipse.birt.chart.model.attribute.LineStyle; |
| import org.eclipse.birt.chart.model.attribute.Marker; |
| import org.eclipse.birt.chart.model.attribute.NumberFormatSpecifier; |
| import org.eclipse.birt.chart.model.attribute.Position; |
| import org.eclipse.birt.chart.model.attribute.TickStyle; |
| import org.eclipse.birt.chart.model.attribute.impl.BoundsImpl; |
| import org.eclipse.birt.chart.model.attribute.impl.ColorDefinitionImpl; |
| import org.eclipse.birt.chart.model.attribute.impl.JavaNumberFormatSpecifierImpl; |
| import org.eclipse.birt.chart.model.attribute.impl.NumberFormatSpecifierImpl; |
| import org.eclipse.birt.chart.model.component.Axis; |
| import org.eclipse.birt.chart.model.component.Series; |
| import org.eclipse.birt.chart.model.component.impl.SeriesImpl; |
| import org.eclipse.birt.chart.model.data.NumberDataSet; |
| import org.eclipse.birt.chart.model.data.SeriesDefinition; |
| import org.eclipse.birt.chart.model.data.impl.NumberDataElementImpl; |
| import org.eclipse.birt.chart.model.data.impl.NumberDataSetImpl; |
| import org.eclipse.birt.chart.model.data.impl.SeriesDefinitionImpl; |
| import org.eclipse.birt.chart.model.impl.ChartWithAxesImpl; |
| import org.eclipse.birt.chart.model.layout.Legend; |
| import org.eclipse.birt.chart.model.layout.Plot; |
| import org.eclipse.birt.chart.model.type.LineSeries; |
| import org.eclipse.birt.chart.model.type.ScatterSeries; |
| import org.eclipse.birt.chart.model.type.impl.ScatterSeriesImpl; |
| import org.eclipse.birt.chart.util.PluginSettings; |
| import org.eclipse.emf.edit.provider.IItemPropertyDescriptor; |
| import org.eclipse.emf.edit.provider.ItemPropertyDescriptor; |
| import org.eclipse.jface.action.Action; |
| import org.eclipse.jface.action.IAction; |
| import org.eclipse.jface.action.MenuManager; |
| import org.eclipse.jface.action.Separator; |
| import org.eclipse.stem.core.model.Decorator; |
| import org.eclipse.stem.core.model.STEMTime; |
| import org.eclipse.stem.definitions.adapters.relativevalue.history.RelativeValueHistoryProvider; |
| import org.eclipse.stem.definitions.adapters.relativevalue.history.RelativeValueHistoryProviderAdapter; |
| import org.eclipse.stem.jobs.simulation.ISimulation; |
| import org.eclipse.stem.ui.adapters.color.STEMColor; |
| import org.eclipse.stem.ui.preferences.VisualizationPreferencePage; |
| import org.eclipse.stem.ui.reports.Activator; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.PaintEvent; |
| import org.eclipse.swt.events.PaintListener; |
| import org.eclipse.swt.graphics.GC; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.widgets.Canvas; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Menu; |
| import org.eclipse.swt.widgets.Widget; |
| import org.eclipse.ui.IWorkbenchActionConstants; |
| |
| /** |
| * TimeSeriesCanvas is a subclass of {@link Canvas} suitable for chart drawings. |
| */ |
| public class TimeSeriesCanvas extends Canvas { |
| |
| protected IDeviceRenderer idr = null; |
| |
| /** |
| * This is the {@link Chart} that plots the relative values. |
| */ |
| protected Chart cm = null; |
| |
| /** |
| * The provider of relative values. |
| */ |
| private RelativeValueHistoryProvider rvhp; |
| |
| private ReportControl control = null; |
| |
| /** |
| * some extra colors |
| */ |
| protected static final ColorDefinition[] colorDefault = { |
| ColorDefinitionImpl.BLUE(), |
| ColorDefinitionImpl.BLACK(), |
| ColorDefinitionImpl.GREY(), |
| ColorDefinitionImpl.CYAN(), |
| ColorDefinitionImpl.ORANGE() |
| }; |
| |
| /** |
| * used to index the line series so we can step through default colors |
| * when a user custom color is not yet assigned |
| */ |
| protected int seriesCount = 0; |
| |
| /** |
| * Log of zero is negative infinity so for each location we will cut off the minimum value |
| * of log(y) at 0.1/POPULATION for display purposes only |
| */ |
| private double minLogScaleValue = 1.0; |
| |
| /** |
| * once time > DEFAULT_AUTOAXIS_THRESHOLD we start to autoreset the time axis |
| * tick marks and scale so we don't have too many tick marks |
| */ |
| private static final int DEFAULT_AUTOAXIS_THRESHOLD = 5; |
| |
| /** |
| * the max value for the time (x) axis |
| */ |
| private int maxTimeAxisValue = DEFAULT_AUTOAXIS_THRESHOLD; |
| |
| /** |
| * the minimum number of time axis ticks |
| */ |
| private static final int MIN_TICKS = 5; |
| |
| /** |
| * the maximum number of time axis ticks after time=DEFAULT_AUTOAXIS_THRESHOLD; |
| */ |
| private static final int MAX_TICKS = 10; |
| |
| /** |
| * used to set the autoaxis tick |
| */ |
| private static final int TICK_TRIGGER = MAX_TICKS/MIN_TICKS; |
| |
| /** |
| * This is a map of properties of the label updated by the selected |
| * {@link Decorator} whose relative value might be plotted (context menu allows us to toggle the individual |
| * properties to plot |
| */ |
| protected Map<ItemPropertyDescriptor,Boolean> propertiesToPlot = new HashMap<ItemPropertyDescriptor,Boolean>(); |
| |
| /** |
| * TODO this is temporary code and should be in user preferences |
| * we want to hide a few of the possible line-series on start up |
| */ |
| private static final String[] HIDE_ON_STARTUP = {"IR","IF","Incidence","Births","Deaths","Population Count", "Disease Deaths"}; |
| private static final Set<String> defaultHideSet = new HashSet<String>(); |
| |
| /** |
| * this is a map of the DataSeries object (keyed by property name) |
| */ |
| protected final Map<String,DataSeries> dataSeriesMap = new HashMap<String,DataSeries>(); |
| |
| |
| /** |
| * These are the cycle numbers that match the relative values that will be |
| * plotted |
| * |
| * @see #relativeValues |
| */ |
| private final List<Integer> cycleNumbers = new ArrayList<Integer>(); |
| |
| /** |
| * Chart generator instance (reference to a singleton) |
| */ |
| Generator gr; |
| |
| |
| Axis yAxisPrimary; |
| Axis xAxisPrimary; |
| |
| |
| /** |
| * A context menu for this view |
| */ |
| Menu popUpMenu = null; |
| |
| // Init a Context Menu Manager |
| final MenuManager contextMenuManager = new MenuManager(); |
| |
| /** |
| * set y axis to a linear scale |
| */ |
| private LinearScaleAction linearTimeAction; |
| /** |
| * set y axis to a log scale |
| */ |
| private LogScaleAction logTimeAction; |
| protected boolean useLinearTimeScale = true; |
| |
| /** |
| * show the legend (true by default) |
| */ |
| private LegendViewAction viewLegend; |
| |
| /** |
| * Clear the properties from the time series |
| */ |
| |
| private ClearAction clearAction; |
| |
| List<DisplayableProperty> displayableProperties; |
| |
| /** |
| * hide the legend |
| */ |
| private LegendHideAction hideLegend; |
| protected boolean showLegend = true; |
| |
| protected Legend legend = null; |
| |
| private static final String defaultKey = ""; |
| |
| Image imgChart = null; |
| |
| /** |
| * Constructor. |
| * |
| * @param parent |
| * the SWT parent of the {@link Widget} |
| */ |
| public TimeSeriesCanvas(final Composite parent) { |
| super(parent, SWT.DOUBLE_BUFFERED | SWT.BORDER); |
| |
| // init |
| for (int i = 0; i < HIDE_ON_STARTUP.length; i ++) { |
| defaultHideSet.add(HIDE_ON_STARTUP[i]); |
| } |
| |
| |
| gr = Generator.instance(); |
| |
| try { |
| idr = PluginSettings.instance().getDevice("dv.SWT"); //$NON-NLS-1$ |
| } catch (final ChartException pex) { |
| Activator.logError("Problem initializing chart", pex); //$NON-NLS-1$ |
| return; |
| } |
| |
| control = (ReportControl) parent; |
| rvhp = control.rvhp; |
| |
| cm = createSimpleLineChart(dataSeriesMap, cycleNumbers, Messages |
| .getString("CC.title")); //$NON-NLS-1$ |
| resetData(); |
| addPaintListener(new PaintListener() { |
| public void paintControl(final PaintEvent pe) { |
| final Composite source = (Composite) pe.getSource(); |
| final org.eclipse.swt.graphics.Rectangle d = source |
| .getClientArea(); |
| |
| if(imgChart != null) imgChart.dispose(); |
| imgChart = new Image(source.getDisplay(), d); |
| idr.setProperty(IDeviceRenderer.GRAPHICS_CONTEXT, new GC( |
| imgChart)); |
| final Bounds bounds = BoundsImpl.create(d.x, d.y, d.width, |
| d.height); |
| bounds.scale(72d / idr.getDisplayServer().getDpiResolution()); |
| // BOUNDS MUST BE SPECIFIED IN POINTS |
| |
| try { |
| gr.render(idr, gr.build(idr.getDisplayServer(), cm, bounds, |
| null, null, null)); |
| pe.gc.drawImage(imgChart, d.x, d.y); |
| } catch (final ChartException ce) { |
| Activator.logError("Problem rendering chart", ce); //$NON-NLS-1$ |
| } |
| |
| } // paintControl |
| } // PaintListener |
| |
| ); |
| |
| //Create a context menu for the canvas |
| createContextMenu(this); |
| |
| } // TimeSeriesCanvas |
| |
| /** |
| * The method which gets the {@link TimeSeriesCanvas}' reports list, and |
| * draws it on the {@link TimeSeriesCanvas}. |
| * |
| */ |
| public synchronized void draw() { |
| |
| |
| // Has a relative value provider been provided? |
| if (rvhp != null) { |
| double maxY = Double.MIN_VALUE; |
| double minY = Double.MAX_VALUE; |
| |
| // Yes |
| // build up the set of properties to plot |
| |
| // First we disable all series so that we only turn on the ones that are enabled. |
| for(DataSeries d: dataSeriesMap.values()) |
| d.hide(); |
| |
| List<ItemPropertyDescriptor> itemList = new ArrayList<ItemPropertyDescriptor>(); |
| itemList.addAll(propertiesToPlot.keySet()); |
| Collections.sort(itemList, new Comparator<ItemPropertyDescriptor>() { |
| |
| public int compare(ItemPropertyDescriptor o1, |
| ItemPropertyDescriptor o2) { |
| return o1.getDisplayName(o1).compareTo(o2.getDisplayName(o2)); |
| } |
| }); |
| |
| |
| List<ItemPropertyDescriptor> displayedPropertyList = new ArrayList<ItemPropertyDescriptor>(); |
| for(ItemPropertyDescriptor property: itemList){ |
| boolean visible = propertiesToPlot.get(property).booleanValue(); |
| displayedPropertyList.add(property); |
| if(visible) { |
| if(dataSeriesMap.containsKey(property.getDisplayName(property))) { |
| DataSeries series = dataSeriesMap.get(property.getDisplayName(property)); |
| // remove it |
| if(!series.isVisible()) { |
| series.show(); |
| } |
| } |
| } else { |
| if(dataSeriesMap.containsKey(property.getDisplayName(property))) { |
| DataSeries series = dataSeriesMap.get(property.getDisplayName(property)); |
| // remove it |
| if(series.isVisible()) { |
| series.hide(); |
| } |
| } |
| }// visible? |
| }// all displayedProperties |
| |
| |
| ///////////////////////////////////////////////////////////// |
| // Log(0.0) is negative infinity so for display purposes only |
| // we set the minimum axis value at 0.1/POPULATION |
| double denom = rvhp.getDenominator(null); |
| if(denom <=0.0) denom = 1.0; |
| minLogScaleValue = 0.1/denom; |
| ///////////////////////////////////////////////////////////// |
| |
| // clear |
| resetData(); |
| |
| |
| |
| boolean setCycles = false; |
| // Get the values for the property to be plotted |
| int maxPoints = 0; |
| STEMTime[] time = rvhp.getAllHistoricTimeValues(); |
| cycleNumbers.clear(); |
| cycleNumbers.add(Integer.valueOf(0)); |
| |
| |
| for (int i = 0; i < displayedPropertyList.size(); i++) { |
| ItemPropertyDescriptor property = displayedPropertyList.get(i); |
| String propertyName = property.getDisplayName(property); |
| |
| //TESTING |
| //System.out.println("looking for "+property.getDisplayName(property)); |
| final double[] doubleValues = rvhp.getHistoricInstances(property,time); |
| |
| // Any values? |
| if (doubleValues.length > 0) { |
| if (maxPoints < doubleValues.length) { |
| maxPoints = doubleValues.length; |
| } |
| |
| // this might be a new display |
| // so we have to fill any empty data set |
| if (!dataSeriesMap.containsKey(property.getDisplayName(property))) { |
| |
| DataSeries series = new DataSeries(propertyName, seriesCount); |
| // we are out of data (properties) so we need to fill in |
| // zeros for any unused lines |
| // fill in the empty arrays |
| if(doubleValues.length < maxPoints) { |
| for(int ii = doubleValues.length; ii < maxPoints-1; ii ++) { |
| // pad with zeros |
| series.addValue(new Double(0.0)); |
| } |
| } |
| |
| dataSeriesMap.put(propertyName, series); |
| } |
| |
| |
| final int earliestCycleNumber = rvhp.getEarliestCycleNumber(); |
| DataSeries series = dataSeriesMap.get(property.getDisplayName(property)); |
| series.setColorDefs(property.getDisplayName(property)); |
| |
| |
| for (int cycleNumber = 0; cycleNumber < doubleValues.length; cycleNumber++) { |
| Double value; |
| double displayValue = doubleValues[cycleNumber]; |
| if (displayValue <= minLogScaleValue && !useLinearTimeScale) { |
| // Log(0.0) is negative infinity so for display purposes only |
| // we set the minimum axis value at 0.1/POPULATION |
| displayValue = minLogScaleValue; |
| } |
| if (useLinearTimeScale) { |
| value = new Double(displayValue); |
| } else { |
| value = new Double(Math.log(displayValue)); |
| } |
| if(maxY < displayValue && series.isVisible()) { |
| maxY = displayValue; |
| } |
| |
| if(minY > displayValue && series.isVisible()) { |
| minY = displayValue; |
| } |
| |
| series.addValue(value); |
| |
| if (!setCycles) { |
| this.cycleNumbers.add(Integer.valueOf( |
| earliestCycleNumber + cycleNumber + 1)); |
| |
| /* |
| * We don't want to add x (time) axis ticks ad infinitum so we we need to dynamically |
| * change the scale. This will toggle between 5-10 ticks every time the max time doubles |
| * past an initial value of maxTimeAxisValue = DEFAULT_AUTOAXIS_THRESHOLD (= 25) |
| */ |
| if(this.cycleNumbers.size()>TICK_TRIGGER*maxTimeAxisValue) { |
| maxTimeAxisValue = this.cycleNumbers.size()+1 ; |
| int stepX = maxTimeAxisValue / MIN_TICKS; |
| xAxisPrimary.getScale().setStep(stepX); |
| } |
| } |
| |
| |
| } // for cycleNumber |
| //Activator.logInformation(" "+series.propertyName+"cycles = "+this.cycleNumbers.size()+" datasize = "+series.getDataSize()); |
| setCycles = true; // we set them only once |
| } else { |
| resetData(); |
| } |
| |
| |
| } // for i properties |
| |
| |
| // Set the new min/max but only for linear scale |
| if(useLinearTimeScale) { |
| if(maxY == Double.MIN_VALUE) { |
| // No plots |
| yAxisPrimary.getScale().setMax(NumberDataElementImpl.create(1.0)); |
| yAxisPrimary.getScale().setMin(NumberDataElementImpl.create(0.0)); |
| yAxisPrimary.getScale().setStep(0.25); |
| setYAxisNumberFormatFromMaxY(1.0); |
| } else if(maxY-minY != 0.0) { |
| yAxisPrimary.getScale().setMax(NumberDataElementImpl.create(maxY)); |
| yAxisPrimary.getScale().setMin(NumberDataElementImpl.create(0.0)); |
| yAxisPrimary.getScale().setStep((maxY-minY)/MIN_TICKS); |
| setYAxisNumberFormatFromMaxY(maxY); |
| } else if(maxY == minY && maxY > 0.0) { |
| yAxisPrimary.getScale().setMax(NumberDataElementImpl.create(maxY)); |
| yAxisPrimary.getScale().setMin(NumberDataElementImpl.create(0.0)); |
| yAxisPrimary.getScale().setStep((maxY)/MIN_TICKS); |
| setYAxisNumberFormatFromMaxY(maxY); |
| } else { |
| yAxisPrimary.getScale().setMax(NumberDataElementImpl.create(1.0)); |
| yAxisPrimary.getScale().setMin(NumberDataElementImpl.create(0.0)); |
| yAxisPrimary.getScale().setStep(0.25); |
| setYAxisNumberFormatFromMaxY(1.0); |
| } |
| } else setYAxisLogNumberFormat(); |
| |
| } // if a relative value provider has been provided |
| else { |
| // No |
| // Need to clear everything |
| resetData(); |
| } |
| |
| // Check each data series in the map to make sure they have enough Y values in them |
| for(DataSeries ds:this.dataSeriesMap.values()) { |
| if(ds.relativeValues.size() < this.cycleNumbers.size()) { |
| ds.relativeValues.clear(); |
| for(int n=0;n<this.cycleNumbers.size();++n) ds.addValue(new Double(0.0)); |
| } |
| } |
| |
| if (!this.isDisposed()) { |
| redraw(); |
| } |
| } // paintControl |
| |
| private void setYAxisLogNumberFormat() { |
| NumberFormatSpecifier jnfs = null; |
| jnfs = NumberFormatSpecifierImpl.create(); |
| yAxisPrimary.setFormatSpecifier(jnfs); |
| } |
| |
| /** |
| * Create the view's context menu and add the action handlers to it. |
| */ |
| private void createContextMenu(final Composite parent) { |
| |
| |
| |
| // --------------------------------------------------------------------- |
| linearTimeAction = new LinearScaleAction(); |
| logTimeAction = new LogScaleAction(); |
| contextMenuManager.add(linearTimeAction); |
| contextMenuManager.add(logTimeAction); |
| // Place Holder for Menu Additions |
| contextMenuManager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); |
| // --------------------------------------------------------------------- |
| viewLegend = new LegendViewAction(); |
| hideLegend = new LegendHideAction(); |
| contextMenuManager.add(viewLegend); |
| contextMenuManager.add(hideLegend); |
| // --------------------------------------------------------------------- |
| // Place Holder for Menu Additions |
| contextMenuManager.add(new Separator( |
| IWorkbenchActionConstants.MB_ADDITIONS)); |
| popUpMenu = contextMenuManager.createContextMenu(parent); |
| // Set the context menu for the viewer |
| parent.setMenu(popUpMenu); |
| |
| } // createContextMenu |
| |
| |
| /** |
| * Update the view's context menu and add the action handlers to it. |
| */ |
| private void updateContextMenu(final Composite parent) { |
| |
| popUpMenu.dispose(); |
| |
| // Init a Context Menu Manager |
| |
| contextMenuManager.removeAll(); |
| // --------------------------------------------------------------------- |
| if(linearTimeAction==null) linearTimeAction = new LinearScaleAction(); |
| if(logTimeAction==null) logTimeAction = new LogScaleAction(); |
| contextMenuManager.add(linearTimeAction); |
| contextMenuManager.add(logTimeAction); |
| contextMenuManager.update(); |
| |
| // --------------------------------------------------------------------- |
| // Place Holder for Menu Additions |
| contextMenuManager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); |
| // --------------------------------------------------------------------- |
| viewLegend = new LegendViewAction(); |
| hideLegend = new LegendHideAction(); |
| contextMenuManager.add(viewLegend); |
| contextMenuManager.add(hideLegend); |
| // --------------------------------------------------------------------- |
| // Place Holder for Menu Additions |
| contextMenuManager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); |
| // --------------------------------------------------------------------- |
| // add the displayable properties |
| if(displayableProperties == null) { |
| displayableProperties = new ArrayList<DisplayableProperty>(); |
| } |
| else { |
| displayableProperties.clear(); |
| } |
| if(rvhp!= null) { |
| //List<IItemPropertyDescriptor> properties = rvhp.getProperties(); |
| contextMenuManager.update(); |
| Iterator<ItemPropertyDescriptor> iter = propertiesToPlot.keySet().iterator(); |
| |
| //for(int i = 0; i < properties.size(); i ++) { |
| while((iter!=null)&&(iter.hasNext())) { |
| ItemPropertyDescriptor nextProp = iter.next(); |
| DisplayableProperty property = new DisplayableProperty(nextProp); |
| displayableProperties.add(property); |
| contextMenuManager.add(property); |
| }// for all properties |
| contextMenuManager.updateAll(true); |
| }// rvhp not null |
| |
| // --------------------------------------------------------------------- |
| |
| // Place Holder for Menu Additions |
| contextMenuManager.add(new Separator( |
| IWorkbenchActionConstants.MB_ADDITIONS)); |
| |
| // --------------------------------------------------------------------- |
| |
| clearAction = new ClearAction(); |
| |
| contextMenuManager.add(clearAction); |
| contextMenuManager.update(); |
| contextMenuManager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); |
| final Menu popUpMenu = contextMenuManager.createContextMenu(parent); |
| |
| // Set the context menu for the viewer |
| parent.setMenu(popUpMenu); |
| |
| |
| } // updateContextMenu |
| |
| |
| /** |
| * @param dataSeriesMap the {@link Map} that will contain the relative values |
| * (0.0-1.0) to plot |
| * @param cycleNumbers |
| * the {@link List} of simulation cycle numbers that match the |
| * relative values |
| * @param seriesIdentifier |
| * the title of the chart |
| * @return a <code>Chart</code> |
| */ |
| public final Chart createSimpleLineChart( |
| final Map<String, DataSeries> dataSeriesMap, |
| final List<Integer> cycleNumbers, final String seriesIdentifier) { |
| |
| final ChartWithAxes chartWithAxes = ChartWithAxesImpl.create(); |
| |
| |
| // Plot |
| chartWithAxes.getBlock().setBackground(ColorDefinitionImpl.create(50, 50,50)); |
| final Plot p = chartWithAxes.getPlot(); |
| p.getClientArea().setBackground( ColorDefinitionImpl.create(0, 0, 0)); |
| |
| // Title |
| // cwaLine.getTitle( ).getLabel( ).getCaption( ).setValue( "Line Chart" |
| // );//$NON-NLS-1$ |
| chartWithAxes.getTitle().setVisible(false); |
| |
| // Legend |
| legend = chartWithAxes.getLegend(); |
| final LineAttributes lia = legend.getOutline(); |
| legend.getText().getFont().setSize(8); |
| lia.setStyle(LineStyle.SOLID_LITERAL); |
| legend.getInsets().set(10, 5, 0, 0); |
| legend.getOutline().setVisible(false); |
| legend.setAnchor(Anchor.NORTH_LITERAL); |
| legend.setPosition(Position.BELOW_LITERAL); |
| legend.getText().setColor(ColorDefinitionImpl.create(255, 255, 255)); |
| legend.getOutline().setColor(ColorDefinitionImpl.create(255, 255, 255)); |
| |
| // ///////// |
| // X-Axis |
| xAxisPrimary = chartWithAxes.getPrimaryBaseAxes()[0]; |
| xAxisPrimary.getMajorGrid().setTickStyle(TickStyle.BELOW_LITERAL); |
| xAxisPrimary.getOrigin().setType(IntersectionType.VALUE_LITERAL); |
| xAxisPrimary.getTitle().setVisible(false); |
| xAxisPrimary.getTitle().getCaption().getFont().setSize(9); |
| xAxisPrimary.getTitle().getCaption().setColor(ColorDefinitionImpl.create(255, 255, 255)); |
| xAxisPrimary.getLabel().getCaption().setColor(ColorDefinitionImpl.create(255, 255, 255)); |
| xAxisPrimary.setType(AxisType.LINEAR_LITERAL); |
| |
| final Series seCategory = SeriesImpl.create(); |
| |
| // new colors |
| seCategory.getLabel().getCaption().setColor(ColorDefinitionImpl.create(255, 255, 255)); |
| seCategory.getLabel().getOutline().setColor(ColorDefinitionImpl.create(255, 255, 255)); |
| // |
| |
| final NumberDataSet categoryValues = NumberDataSetImpl |
| .create(cycleNumbers); |
| seCategory.setDataSet(categoryValues); |
| final SeriesDefinition sdX = SeriesDefinitionImpl.create(); |
| |
| xAxisPrimary.getSeriesDefinitions().add(sdX); |
| sdX.getSeries().add(seCategory); |
| |
| // //////// |
| // Y-Axis |
| this.yAxisPrimary = chartWithAxes.getPrimaryOrthogonalAxis(xAxisPrimary); |
| yAxisPrimary.getMajorGrid().setTickStyle(TickStyle.LEFT_LITERAL); |
| // end Y-Series |
| |
| xAxisPrimary.getScale().setMin(NumberDataElementImpl.create(0.0)); |
| xAxisPrimary.getScale().setStep(1); |
| |
| //xAxisPrimary.getScale().setMax(NumberDataElementImpl.create(TICK_TRIGGER*DEFAULT_AUTOAXIS_THRESHOLD)); |
| //int numTicks = TICK_TRIGGER*DEFAULT_AUTOAXIS_THRESHOLD/MIN_TICKS; |
| // |
| |
| |
| |
| xAxisPrimary.getLabel().getCaption().getFont().setSize(9); |
| // xAxisPrimary.getLabel().getCaption().setColor(foreGround); |
| setTimeLabel(); |
| |
| yAxisPrimary.getScale().setMin(NumberDataElementImpl.create(0.0)); |
| yAxisPrimary.getScale().setMax(NumberDataElementImpl.create(1.0)); |
| yAxisPrimary.getScale().setStep(0.25); |
| yAxisPrimary.getLabel().getCaption().getFont().setSize(9); |
| yAxisPrimary.getLabel().getCaption().setColor(ColorDefinitionImpl.create(255, 255, 255)); |
| yAxisPrimary.getTitle().getCaption().setColor(ColorDefinitionImpl.create(255, 255, 255)); |
| setYAxisNumberFormatFromMaxY(1.0); |
| |
| |
| // yAxisPrimary.getMajorGrid().getLineAttributes().setColor(foreGround); |
| |
| // for now get ready to create only one line - we have no data yet. |
| // we will add more lines as we need them |
| // handle null |
| if(!dataSeriesMap.containsKey(defaultKey)) { |
| DataSeries series = new DataSeries(defaultKey, seriesCount); |
| dataSeriesMap.put(defaultKey, series); |
| } |
| |
| return chartWithAxes; |
| } // createSimpleLineChart |
| |
| private void setYAxisNumberFormatFromMaxY(double maxY) { |
| JavaNumberFormatSpecifier jnfs = null; |
| if(maxY < 0.001) |
| jnfs = JavaNumberFormatSpecifierImpl.create("0.####E0"); |
| else |
| jnfs = JavaNumberFormatSpecifierImpl.create("0.#####"); |
| yAxisPrimary.setFormatSpecifier(jnfs); |
| |
| } |
| |
| void setTimeLabel() { |
| String val = ""; |
| if (control != null) { |
| final ISimulation sim = control.simulation; |
| if (sim != null) { |
| final long timeDelta = sim.getScenario().getSequencer() |
| .getTimeDelta(); |
| |
| if (timeDelta < STEMTime.Units.MINUTE.getMilliseconds()) { |
| val = "secs"; |
| xAxisPrimary.getTitle().setVisible(true); |
| xAxisPrimary.getTitle().getCaption().setValue(val); |
| return; |
| } else if (timeDelta < STEMTime.Units.HOUR.getMilliseconds()) { |
| val = "mins"; |
| xAxisPrimary.getTitle().setVisible(true); |
| xAxisPrimary.getTitle().getCaption().setValue(val); |
| return; |
| } else if (timeDelta < STEMTime.Units.DAY.getMilliseconds()) { |
| val = "hrs"; |
| xAxisPrimary.getTitle().setVisible(true); |
| xAxisPrimary.getTitle().getCaption().setValue(val); |
| return; |
| |
| } else if (timeDelta <= STEMTime.Units.WEEK.getMilliseconds()) { |
| val = "days"; |
| xAxisPrimary.getTitle().setVisible(true); |
| xAxisPrimary.getTitle().getCaption().setValue(val); |
| return; |
| |
| } else if (timeDelta < 4 * STEMTime.Units.WEEK |
| .getMilliseconds()) { |
| val = "weeks"; |
| xAxisPrimary.getTitle().setVisible(true); |
| xAxisPrimary.getTitle().getCaption().setValue(val); |
| return; |
| } else if (timeDelta < STEMTime.Units.YEAR.getMilliseconds()) { |
| val = "months"; |
| xAxisPrimary.getTitle().setVisible(true); |
| xAxisPrimary.getTitle().getCaption().setValue(val); |
| return; |
| } else { |
| val = "years"; |
| xAxisPrimary.getTitle().setVisible(true); |
| xAxisPrimary.getTitle().getCaption().setValue(val); |
| return; |
| } |
| |
| } // sim ! null |
| }// control ! null |
| |
| } // setTimeLabel() |
| |
| |
| |
| |
| /** |
| * @param rvhp |
| * @param propertiesToPlotList |
| */ |
| public synchronized void setDataSourceAndRedraw( |
| final RelativeValueHistoryProviderAdapter rvhp, |
| final List<IItemPropertyDescriptor> propertiesToPlotList) { |
| |
| |
| |
| if(rvhp!=this.rvhp) { |
| this.rvhp= rvhp; |
| removeDataSeries(); |
| this.propertiesToPlot.clear(); |
| this.propertiesToPlot = addProperties(propertiesToPlotList); |
| } |
| |
| updateContextMenu(this); |
| draw(); |
| } // setDataSourceAndRedraw |
| |
| /** |
| * Filters a list of ItemPropertyDescriptors to show preferred properties if |
| * at least one exists. If not, just returns the entire list. |
| * |
| * @return filteredList<ItemPropertyDescriptor> |
| */ |
| protected List<ItemPropertyDescriptor> filterPreferredProperties( |
| final List<ItemPropertyDescriptor> fullList) { |
| final List<ItemPropertyDescriptor> propertyList = new ArrayList<ItemPropertyDescriptor>(); |
| // Are any of the labels in the preference set? |
| final Set<String> prefSet = VisualizationPreferencePage |
| .getPreferenceSet(); |
| |
| for (int i = 0; i < fullList.size(); i++) { |
| final ItemPropertyDescriptor property = fullList.get(i); |
| if (prefSet.contains(property.getDisplayName(property))) { |
| propertyList.add(property); |
| } |
| } |
| // if we didn't find any in the preferences then just add them all |
| if (propertyList.size() == 0) { |
| propertyList.addAll(fullList); |
| } |
| return propertyList; |
| } |
| |
| |
| /** |
| * Filters a list of ItemPropertyDescriptors to show preferred properties if |
| * at least one exists. If not, just returns the entire list. |
| * |
| * @return filteredList<ItemPropertyDescriptor> |
| */ |
| protected Map<ItemPropertyDescriptor,Boolean> addProperties(final List<IItemPropertyDescriptor> propertiesToPlotList) { |
| |
| for (int i = 0; i < propertiesToPlotList.size(); i++) { |
| final ItemPropertyDescriptor property = (ItemPropertyDescriptor)propertiesToPlotList.get(i); |
| if(!propertiesToPlot.containsKey(property)) { |
| boolean visible = true; |
| if(defaultHideSet.contains(property.getDisplayName(property))) visible = false; |
| Boolean bObj = Boolean.valueOf(visible); //default visibility |
| this.propertiesToPlot.put(property,bObj); |
| } |
| } |
| |
| return this.propertiesToPlot; |
| } |
| |
| /** |
| * Disposes the Color objects |
| */ |
| @Override |
| public void dispose() { |
| super.dispose(); |
| } |
| |
| protected void resetData() { |
| clearData(); |
| // for now get ready to create only one line - we have no data yet. |
| // we will add more lines as we need them |
| |
| if(!dataSeriesMap.containsKey(defaultKey)) { |
| DataSeries series = new DataSeries(defaultKey, seriesCount); |
| |
| dataSeriesMap.put(defaultKey, series); |
| } |
| // handle null |
| |
| cycleNumbers.add(Integer.valueOf(0)); |
| } |
| |
| /** |
| * clearsAllData |
| */ |
| private void clearData() { |
| |
| Iterator<String> iter = dataSeriesMap.keySet().iterator(); |
| while((iter!=null)&&(iter.hasNext())) { |
| String key = iter.next(); |
| DataSeries series = dataSeriesMap.get(key); |
| series.relativeValues.clear(); |
| series.addValue(new Double(0.0)); |
| xAxisPrimary.getScale().setMin(NumberDataElementImpl.create(0.0)); |
| maxTimeAxisValue = DEFAULT_AUTOAXIS_THRESHOLD; |
| //xAxisPrimary.getScale().setMax(NumberDataElementImpl.create(TICK_TRIGGER*DEFAULT_AUTOAXIS_THRESHOLD)); |
| //int numTicks = TICK_TRIGGER*DEFAULT_AUTOAXIS_THRESHOLD/MIN_TICKS; |
| xAxisPrimary.getScale().setStep(1); |
| } |
| cycleNumbers.clear(); |
| } |
| |
| /** |
| * removeData is called when switching between decorators. For example, when switching from display of a disease to dispaly of a population, |
| * the properties are different and the lineSeries, propetries, and axis labels must be removed. SEE also: DataSeries.removeLineSeries() |
| */ |
| private void removeDataSeries() { |
| if(displayableProperties!=null) displayableProperties.clear(); |
| Iterator<ItemPropertyDescriptor> iter = propertiesToPlot.keySet().iterator(); |
| while((iter!=null)&&(iter.hasNext())) { |
| ItemPropertyDescriptor propertyToRemove = iter.next(); |
| String key = propertyToRemove.getDisplayName(propertyToRemove); |
| DataSeries series = dataSeriesMap.get(key); |
| //series.hide(); |
| if(series != null) { |
| series.removeLineSeries(key); |
| //series.lineSeries.setVisible(false); |
| dataSeriesMap.remove(key); |
| series = null; |
| } // if series not null |
| } |
| propertiesToPlot.clear(); |
| }//removeDataSeries |
| |
| |
| |
| /** |
| * toggle the scale from logarithmic to linear |
| */ |
| void toggleAxisScale() { |
| if (useLinearTimeScale) { //Switch to logarithmic scale |
| logTimeAction.setChecked(true); |
| linearTimeAction.setChecked(false); |
| //Just using the following axis type, to move to log scale, didn't work |
| //yAxisPrimary.setType(AxisType.LOGARITHMIC_LITERAL); |
| yAxisPrimary.getScale().setMin(null); |
| yAxisPrimary.getScale().setMax(null); |
| yAxisPrimary.getScale().unsetStep(); |
| yAxisPrimary.getScale().unsetStepNumber(); |
| } |
| else { //Switch to linear scale |
| logTimeAction.setChecked(false); |
| linearTimeAction.setChecked(true); |
| //yAxisPrimary.setType(AxisType.LINEAR_LITERAL); |
| yAxisPrimary.getScale().setMin(NumberDataElementImpl.create(0.0)); |
| yAxisPrimary.getScale().setMax(NumberDataElementImpl.create(1.0)); |
| yAxisPrimary.getScale().setStep(0.25); |
| } |
| useLinearTimeScale = !useLinearTimeScale; |
| this.draw(); |
| } |
| |
| |
| /** |
| * toggle the scale from logarithmic to linear |
| */ |
| void toggleLegend() { |
| if (showLegend) { //Switch to hide |
| viewLegend.setChecked(false); |
| hideLegend.setChecked(true); |
| legend.setVisible(false); |
| } |
| else { //Switch to view |
| viewLegend.setChecked(true); |
| hideLegend.setChecked(false); |
| legend.setVisible(true); |
| } |
| showLegend = !showLegend; |
| this.draw(); |
| } |
| |
| /** |
| * switch to linear plot |
| * |
| */ |
| protected class LinearScaleAction extends Action { |
| public LinearScaleAction() |
| { |
| super(Messages.getString("ContextMenu.LinearTimeScale"), IAction.AS_CHECK_BOX); |
| setChecked(useLinearTimeScale); |
| } |
| |
| /** |
| * @see org.eclipse.jface.action.Action#getText() |
| */ |
| @Override |
| public String getText() { |
| return Messages.getString("ContextMenu.LinearTimeScale"); |
| } |
| |
| /** |
| * @see org.eclipse.jface.action.Action#run() |
| */ |
| @Override |
| public void run() { |
| if (useLinearTimeScale) { |
| setChecked(true); |
| //Nothing to do. It's already linear-time. |
| } |
| else { |
| toggleAxisScale(); |
| } |
| } |
| } //LinearScaleAction |
| |
| /** |
| * switch to semi-log plot (log scale on y axis) |
| * |
| */ |
| class LogScaleAction extends Action { |
| public LogScaleAction() |
| { |
| super(Messages.getString("ContextMenu.LogTimeScale"), IAction.AS_CHECK_BOX); |
| setChecked(!useLinearTimeScale); |
| } |
| |
| /** |
| * @see org.eclipse.jface.action.Action#getText() |
| */ |
| @Override |
| public String getText() { |
| return Messages.getString("ContextMenu.LogTimeScale"); |
| } |
| |
| /** |
| * @see org.eclipse.jface.action.Action#run() |
| */ |
| @Override |
| public void run() { |
| if (!useLinearTimeScale) { |
| setChecked(true); |
| //Nothing to do. It's already log-time. |
| } |
| else { |
| toggleAxisScale(); |
| } |
| } |
| }//LogScaleAction |
| |
| /** |
| * Action to show the legend |
| */ |
| protected class LegendViewAction extends Action { |
| public LegendViewAction() |
| { |
| super(Messages.getString("ContextMenu.ShowLegend"), IAction.AS_CHECK_BOX); |
| setChecked(showLegend); |
| } |
| |
| /** |
| * @see org.eclipse.jface.action.Action#getText() |
| */ |
| @Override |
| public String getText() { |
| return Messages.getString("ContextMenu.ShowLegend"); |
| } |
| |
| /** |
| * @see org.eclipse.jface.action.Action#run() |
| */ |
| @Override |
| public void run() { |
| if (showLegend) { |
| setChecked(true); |
| //Nothing to do. It's already linear-time. |
| } |
| else { |
| toggleLegend(); |
| } |
| } |
| } //LegendViewAction |
| |
| /** |
| * Action to hide the legend |
| */ |
| class LegendHideAction extends Action { |
| public LegendHideAction() |
| { |
| super(Messages.getString("ContextMenu.HideLegend"), IAction.AS_CHECK_BOX); |
| setChecked(!showLegend); |
| } |
| |
| /** |
| * @see org.eclipse.jface.action.Action#getText() |
| */ |
| @Override |
| public String getText() { |
| return Messages.getString("ContextMenu.HideLegend"); |
| } |
| |
| /** |
| * @see org.eclipse.jface.action.Action#run() |
| */ |
| @Override |
| public void run() { |
| if (!showLegend) { |
| setChecked(true); |
| //Nothing to do. It's already log-time. |
| } |
| else { |
| toggleLegend(); |
| } |
| } |
| }//LegendHideAction |
| |
| |
| /** |
| * DisplayableProperty |
| * |
| */ |
| protected class DisplayableProperty extends Action |
| { |
| ItemPropertyDescriptor property = null; |
| public DisplayableProperty(ItemPropertyDescriptor property) |
| { |
| super(property.getDisplayName(property), IAction.AS_CHECK_BOX); |
| this.property = property; |
| if(propertiesToPlot.containsKey(property)) { |
| setChecked(propertiesToPlot.get(property).booleanValue()); |
| } else { |
| propertiesToPlot.put(property,new Boolean(true)); |
| setChecked(propertiesToPlot.get(property).booleanValue()); |
| } |
| |
| } |
| |
| /** |
| * @see org.eclipse.jface.action.Action#getText() |
| */ |
| @Override |
| public String getText() { |
| return property.getDisplayName(property); |
| } |
| |
| /** |
| * Toggle the state |
| * @see org.eclipse.jface.action.Action#run() |
| */ |
| @Override |
| public void run() { |
| boolean state = !propertiesToPlot.get(property).booleanValue(); |
| propertiesToPlot.put(property, Boolean.valueOf(state)); |
| setChecked(state); |
| |
| draw(); |
| } |
| public ItemPropertyDescriptor getProperty() { |
| return property; |
| } |
| }// DisplayableProperty |
| |
| |
| /** |
| * Action to show the legend |
| */ |
| protected class ClearAction extends Action { |
| public ClearAction() |
| { |
| super(Messages.getString("ContextMenu.Clear"), IAction.AS_PUSH_BUTTON); |
| } |
| |
| /** |
| * @see org.eclipse.jface.action.Action#getText() |
| */ |
| @Override |
| public String getText() { |
| return Messages.getString("ContextMenu.Clear"); |
| } |
| |
| /** |
| * @see org.eclipse.jface.action.Action#run() |
| */ |
| @SuppressWarnings("boxing") |
| @Override |
| public void run() { |
| for(ItemPropertyDescriptor ipd:propertiesToPlot.keySet()) { |
| boolean state = !propertiesToPlot.get(ipd).booleanValue(); |
| propertiesToPlot.put(ipd, false); |
| for(DisplayableProperty dp:displayableProperties) |
| if(dp.getProperty().equals(ipd)) {dp.setChecked(false);break;} |
| draw(); |
| } |
| } |
| } //ClearAction |
| |
| |
| protected class DataSeries |
| { |
| public String propertyName = ""; |
| |
| |
| public List<Double> relativeValues = new ArrayList<Double>(); |
| public LineSeries lineSeries = null; |
| private boolean visible = true; |
| private SeriesDefinition sdY = null; |
| |
| private int seriesIndex = 0; |
| |
| |
| public boolean isVisible() { |
| return visible; |
| } |
| |
| /** |
| * |
| * @param propertyName |
| * @param index |
| */ |
| public DataSeries(String propertyName, int index) { |
| this.propertyName = propertyName; |
| this.seriesIndex = index; |
| seriesCount ++; |
| relativeValues = new ArrayList<Double>(); |
| relativeValues.add(new Double(0.0)); |
| addLineSeries(propertyName); |
| } |
| |
| public void addValue(Double val) { |
| if(relativeValues==null) relativeValues = new ArrayList<Double>(); |
| relativeValues.add(val); |
| } |
| |
| /** |
| * @param propertyName |
| * |
| */ |
| @SuppressWarnings("cast") |
| public void addLineSeries(final String propertyName) { |
| final NumberDataSet orthoValues = NumberDataSetImpl |
| .create(relativeValues); |
| if(lineSeries == null) lineSeries = (ScatterSeries) ScatterSeriesImpl.create(); |
| lineSeries.setDataSet(orthoValues); |
| |
| lineSeries.getLineAttributes().setVisible(true); |
| |
| // Assign the line color |
| // based on selected property. Default is Blue |
| setColorDefs(propertyName); |
| |
| // replaces deprecated code: lineSeries.getMarker().setVisible(false); |
| if (!lineSeries.getMarkers().isEmpty()) { |
| Marker marker = (Marker)lineSeries.getMarkers().get(0); |
| marker.setVisible(false); |
| } |
| |
| // the series def |
| sdY = SeriesDefinitionImpl.create(); |
| //sdY.getSeriesPalette().update(-2); |
| sdY.getSeries().add(lineSeries); |
| yAxisPrimary.getSeriesDefinitions().add(sdY); |
| setTimeLabel(); |
| return; |
| } |
| |
| /** |
| * |
| * @param propertyName |
| */ |
| public void removeLineSeries(final String propertyName) { |
| //visible = false; |
| seriesIndex = 0; |
| // ?? relativeValues.clear(); |
| if(lineSeries!=null) lineSeries.getLineAttributes().setVisible(false); |
| if(yAxisPrimary !=null) yAxisPrimary.getSeriesDefinitions().remove(sdY); |
| if(sdY != null) sdY.getSeries().remove(lineSeries); |
| lineSeries = null; |
| sdY = null; |
| |
| return; |
| } |
| |
| |
| |
| /** |
| * in response to user action temporarily remove the line series from the graph |
| */ |
| public void hide() { |
| lineSeries.setVisible(false); |
| visible = false; |
| }// hide |
| |
| /** |
| * in response to user action add back the line series to the graph |
| */ |
| public void show() { |
| lineSeries.setVisible(true); |
| visible = true; |
| }// show |
| |
| |
| /** |
| * Sets the colors for a n array of LineSeries given the property to Plot |
| * for each. Try to set color from the preferences (if specified for that |
| * property) otherwise sets line color to blue. |
| * @param propertyName |
| * |
| */ |
| public void setColorDefs(String propertyName) { |
| // the default line color |
| |
| // if possible get color from preferences |
| final Map<String, STEMColor> colorMap = VisualizationPreferencePage.getColorMapping(); |
| // pick a default color |
| int colorIndex = seriesIndex % colorDefault.length; |
| ColorDefinition color = colorDefault[colorIndex]; |
| |
| // Check for a prefix match |
| for (String key : colorMap.keySet()) { |
| if((propertyName.startsWith(key))&&(key.length()>=1)) { |
| // might be right |
| String tail = propertyName.substring(key.length(), propertyName.length()); |
| if((tail==null)||(tail.length()==0)) { |
| final STEMColor c = colorMap.get(key); |
| color = ColorDefinitionImpl.create((int)(c.getRed() * 255.0), (int)(c.getGreen() * 255.0), (int)(c.getBlue() * 255.0)); |
| break; |
| } |
| |
| boolean hit = true; |
| for (char c : tail.toCharArray()) { |
| if (!Character.isDigit(c)) { |
| hit = false; |
| break; |
| } |
| }// all characters in tail |
| if(hit){ |
| final STEMColor c = colorMap.get(key); |
| color = ColorDefinitionImpl.create((int)(c.getRed() * 255.0), (int)(c.getGreen() * 255.0), (int)(c.getBlue() * 255.0)); |
| break; |
| } |
| } |
| } |
| |
| // if (colorMap.containsKey(propertyName)) { |
| // final Color c = colorMap.get(propertyName); |
| // color = ColorDefinitionImpl.create(c.getRed(), c.getGreen(), c.getBlue()); |
| // } |
| |
| this.lineSeries.setSeriesIdentifier(propertyName); |
| this.lineSeries.getLineAttributes().setColor(color); |
| return; |
| }// getColorDef |
| |
| /** |
| * length of the series |
| * @return number of data points |
| */ |
| public int getDataSize() { |
| return relativeValues.size(); |
| } |
| // Accessors |
| public String getPropertyName() { |
| return propertyName; |
| } |
| |
| public void setPropertyName(String propertyName) { |
| this.propertyName = propertyName; |
| } |
| |
| public List<Double> getRelativeValues() { |
| return relativeValues; |
| } |
| |
| public void setRelativeValues(List<Double> relativeValues) { |
| this.relativeValues = relativeValues; |
| } |
| |
| public LineSeries getLineSeries() { |
| return lineSeries; |
| } |
| |
| public void setLineSeries(LineSeries lineSeries) { |
| this.lineSeries = lineSeries; |
| } |
| |
| |
| }// DataSeries |
| |
| } // TimeSeriesCanvas |