package gov.nasa.arc.mct.fastplot.scatter;
import gov.nasa.arc.mct.components.FeedProvider.RenderingInfo;
import gov.nasa.arc.mct.fastplot.bridge.AbstractAxis;
import gov.nasa.arc.mct.fastplot.bridge.AbstractAxis.AxisVisibleOrientation;
import gov.nasa.arc.mct.fastplot.bridge.AbstractAxisBoundManager;
import gov.nasa.arc.mct.fastplot.bridge.AbstractPlotDataManager;
import gov.nasa.arc.mct.fastplot.bridge.AbstractPlotDataSeries;
import gov.nasa.arc.mct.fastplot.bridge.AbstractPlotLine;
import gov.nasa.arc.mct.fastplot.bridge.AbstractPlottingPackage;
import gov.nasa.arc.mct.fastplot.bridge.LegendManager;
import gov.nasa.arc.mct.fastplot.bridge.PlotAbstraction;
import gov.nasa.arc.mct.fastplot.bridge.PlotConstants;
import gov.nasa.arc.mct.fastplot.bridge.PlotConstants.LimitAlarmState;
import gov.nasa.arc.mct.fastplot.bridge.PlotConstants.NonTimeAxisSubsequentBoundsSetting;
import gov.nasa.arc.mct.fastplot.bridge.PlotConstants.XAxisMaximumLocationSetting;
import gov.nasa.arc.mct.fastplot.bridge.PlotConstants.YAxisMaximumLocationSetting;
import gov.nasa.arc.mct.fastplot.bridge.PlotLimitManager;
import gov.nasa.arc.mct.fastplot.bridge.PlotObserver;
import gov.nasa.arc.mct.fastplot.bridge.PlotViewActionListener;
import gov.nasa.arc.mct.fastplot.bridge.controls.AbstractPlotLocalControl;
import gov.nasa.arc.mct.fastplot.bridge.controls.AbstractPlotLocalControl.AttachmentLocation;
import gov.nasa.arc.mct.fastplot.bridge.controls.AbstractPlotLocalControlsManager;
import gov.nasa.arc.mct.fastplot.bridge.controls.PlotLocalControlsManagerImpl;
import gov.nasa.arc.mct.fastplot.settings.PlotConfiguration;
import gov.nasa.arc.mct.fastplot.settings.PlotConfigurationDelegator;
import gov.nasa.arc.mct.fastplot.settings.PlotSettings;
import gov.nasa.arc.mct.fastplot.utils.AbbreviatingPlotLabelingAlgorithm;
import gov.nasa.arc.mct.fastplot.view.Axis;
import gov.nasa.arc.mct.fastplot.view.legend.AbstractLegendEntry;
import java.awt.Color;
import java.awt.Font;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.SpringLayout;
import plotter.xy.ScatterXYPlotLine;
import plotter.xy.SimpleXYDataset;
import plotter.xy.XYPlot;
public class ScatterPlot extends PlotConfigurationDelegator implements AbstractPlottingPackage {
private ScatterPlotDataManager dataManager = new ScatterPlotDataManager(this);
private ArrayList<PlotObserver> observers = new ArrayList<PlotObserver>();
private Set<String> knownDataSeries = new HashSet<String>();
private PlotAbstraction abstraction;
private XYPlot plotPanel;
private ImplicitTimeAxis timeAxis = new ImplicitTimeAxis();
private AbbreviatingPlotLabelingAlgorithm plotLabelingAlgorithm = new AbbreviatingPlotLabelingAlgorithm();
private LegendManager legendManager = new LegendManager(plotLabelingAlgorithm);
private double initialNonTimeMin;
private double initialNonTimeMax;
private PlotLocalControlsManagerImpl localControls = new PlotLocalControlsManagerImpl();
private PlotViewActionListener actionListener;
private Map<AxisVisibleOrientation, Collection<AbstractAxisBoundManager>> boundManagers =
new HashMap<AxisVisibleOrientation, Collection<AbstractAxisBoundManager>>();
public ScatterPlot() {
this (new PlotSettings());
}
public ScatterPlot(PlotConfiguration delegate) {
super(delegate);
if (delegate instanceof PlotAbstraction) {
setPlotAbstraction((PlotAbstraction) delegate);
}
timeAxis.setStart(delegate.getMinTime());
timeAxis.setEnd(delegate.getMaxTime());
initialNonTimeMin = delegate.getMinNonTime();
initialNonTimeMax = delegate.getMaxNonTime();
}
@Override
public void registerObservor(PlotObserver o) {
observers.add(o);
}
@Override
public void removeObserver(PlotObserver o) {
observers.remove(o);
}
@Override
public void notifyObserversTimeChange() {
//TODO need to implement for non-time?
}
@Override
public void createChart(Font timeAxisFont, int plotLineThickness,
Color plotBackgroundFrameColor, Color plotAreaBackgroundColor,
int timeAxisIntercept, Color timeAxisColor,
Color timeAxisLabelColor, Color nonTimeAxisLabelColor,
String timeAxisDataFormat, Color nonTimeAxisColor,
Color gridLineColor, int minSamplesForAutoScale,
boolean isCompressionEnabled, boolean isTimeLabelsEnabled,
boolean isLocalControlEnabled, PlotAbstraction thePlotAbstraction,
AbbreviatingPlotLabelingAlgorithm thePlotLabelingAlgorithm) {
ScatterPlotObjects objects = new ScatterPlotObjects(this);
objects.setAxisRepresentation(timeAxisFont, nonTimeAxisColor);
plotPanel = objects.getXYPlot();
setPlotAbstraction(thePlotAbstraction);
setDelegate(thePlotAbstraction);
setupAxisBounds();
timeAxis.setStart(thePlotAbstraction.getMinTime());
timeAxis.setEnd(thePlotAbstraction.getMaxTime());
legendManager.setOpaque(false);
actionListener = new PlotViewActionListener(this);
setupAxisBoundsManagers();
}
private void setupAxisBoundsManagers() {
for (AbstractAxis axis : getAxes()) {
AxisVisibleOrientation o = axis.getVisibleOrientation();
if (o != null) {
List<AbstractAxisBoundManager> bounds = new ArrayList<AbstractAxisBoundManager>(2);
for (boolean maximal : new boolean[] { false, true } ) {
NonTimeAxisSubsequentBoundsSetting setting = maximal ?
getNonTimeAxisSubsequentMaxSetting() :
getNonTimeAxisSubsequentMinSetting();
switch (setting) {
case AUTO:
bounds.add(new NonTimeAutoExpandBoundManager(this, axis, maximal));
break;
case FIXED:
bounds.add(new NonTimeFixedBoundManager(this, axis, maximal));
break;
case SEMI_FIXED:
bounds.add(new NonTimeSemiFixedBoundManager(this, axis, maximal));
break;
}
}
boundManagers.put(o, bounds);
}
}
}
private void setupAxisBounds() {
// Swap depending on MAXIMUM_AT_RIGHT etc
double independentBounds[] = { getMinNonTime(), getMaxNonTime() };
double dependentBounds[] = { getMinDependent(), getMaxDependent() };
int minXIndex = getXAxisMaximumLocation() == XAxisMaximumLocationSetting.MAXIMUM_AT_RIGHT ? 0 : 1;
int minYIndex = getYAxisMaximumLocation() == YAxisMaximumLocationSetting.MAXIMUM_AT_TOP ? 0 : 1;
plotPanel.getXAxis().setStart(independentBounds[ minXIndex]);
plotPanel.getXAxis().setEnd (independentBounds[1 - minXIndex]);
plotPanel.getYAxis().setStart(dependentBounds [ minYIndex]);
plotPanel.getYAxis().setEnd (dependentBounds [1 - minYIndex]);
}
@Override
public JComponent getPlotComponent() {
return plotPanel;
}
@Override
public void addDataSet(String dataSetName, Color plottingColor) {
dataManager.addDataSet(dataSetName, plottingColor);
AbstractPlotDataSeries series = dataManager.getNamedDataSeries(dataSetName);
if (series != null && series instanceof ScatterPlotDataSeries) {
((ScatterPlotDataSeries) series).getPlotLine().setColor(plottingColor);
//series.getLegendEntry().setDataSetName(dataSetName);
}
if (dataSetName.contains(PlotConstants.NON_TIME_FEED_SEPARATOR)) {
knownDataSeries.add(dataSetName);
dataSetName = dataSetName.split(PlotConstants.NON_TIME_FEED_SEPARATOR)[1];
}
knownDataSeries.add(dataSetName);
}
@Override
public void addDataSet(String lowerCase, Color plottingColor,
String displayName) {
addDataSet(lowerCase, plottingColor);
AbstractPlotDataSeries series = dataManager.getNamedDataSeries(lowerCase);
if (series != null) {
series.getLegendEntry().setBaseDisplayName(displayName);
}
plotPanel.revalidate();
}
@Override
public boolean isKnownDataSet(String setName) {
return true;//lotDataManager.getNamedDataSeries(setName) != null;
}
@Override
public void updateLegend(String dataSetName, RenderingInfo info) {
}
@Override
public void refreshDisplay() {
}
@Override
public int getDataSetSize() {
return dataManager.size();
}
@Override
public LimitAlarmState getDependentMaxAlarmState() {
return LimitAlarmState.NO_ALARM; // TODO - need limit alarm for scatterplot
}
@Override
public LimitAlarmState getDependentMinAlarmState() {
return LimitAlarmState.NO_ALARM; // TODO - need limit alarm for scatterplot
}
@Override
public GregorianCalendar getCurrentTimeAxisMin() {
GregorianCalendar gc = new GregorianCalendar();
gc.setTimeInMillis(timeAxis.getStartAsLong());
return gc;
}
@Override
public GregorianCalendar getCurrentTimeAxisMax() {
GregorianCalendar gc = new GregorianCalendar();
gc.setTimeInMillis(timeAxis.getEndAsLong());
return gc; }
@Override
public void showTimeSyncLine(GregorianCalendar time) {
// TODO Auto-generated method stub
}
@Override
public void removeTimeSyncLine() {
// TODO Auto-generated method stub
}
@Override
public double getInitialNonTimeMinSetting() {
return initialNonTimeMin;
}
@Override
public double getInitialNonTimeMaxSetting() {
return initialNonTimeMax;
}
@Override
public long getInitialTimeMinSetting() {
return super.getMinTime();
}
@Override
public long getInitialTimeMaxSetting() {
return super.getMaxTime();
}
@Override
public boolean isTimeSyncLineVisible() {
// TODO Auto-generated method stub
return false;
}
@Override
public void setPlotAbstraction(PlotAbstraction plotView) {
abstraction = plotView;
abstraction.setPlotTimeAxis(timeAxis);
}
@Override
public void notifyGlobalTimeSyncFinished() {
}
@Override
public boolean inTimeSyncMode() {
return false;
}
@Override
public double getNonTimeMaxDataValueCurrentlyDisplayed() {
return Math.max(
Math.max(plotPanel.getXAxis().getStart(), plotPanel.getYAxis().getStart()),
Math.max(plotPanel.getXAxis().getEnd(), plotPanel.getYAxis().getEnd()));
}
@Override
public double getNonTimeMinDataValueCurrentlyDisplayed() {
return Math.min(
Math.min(plotPanel.getXAxis().getStart(), plotPanel.getYAxis().getStart()),
Math.min(plotPanel.getXAxis().getEnd(), plotPanel.getYAxis().getEnd()));
}
@Override
public void setCompressionEnabled(boolean state) {
// Ignore compression! Makes no sense on scatter plot
}
@Override
public boolean isCompressionEnabled() {
return false;
}
@Override
public void informUpdateCachedDataStreamStarted() {
}
@Override
public void informUpdateCacheDataStreamCompleted() {
}
@Override
public void informUpdateFromLiveDataStreamStarted() {
}
@Override
public void informUpdateFromLiveDataStreamCompleted() {
for (PlotObserver o : this.observers) {
o.dataPlotted();
}
}
@Override
public void setTimeAxisStartAndStop(long startTime, long endTime) {
timeAxis.setStart(startTime);
timeAxis.setEnd(endTime);
}
@Override
public void clearAllDataFromPlot() {
// TODO Auto-generated method stub
}
@Override
public void pause(boolean b) {
// TODO Auto-generated method stub
}
@Override
public boolean isPaused() {
// TODO Auto-generated method stub
return false;
}
@Override
public Axis getNonTimeAxis() {
// TODO Auto-generated method stub
return null;
}
@Override
public void updateResetButtons() {
// TODO Auto-generated method stub
}
@Override
public void setPlotLabelingAlgorithm(
AbbreviatingPlotLabelingAlgorithm thePlotLabelingAlgorithm) {
plotLabelingAlgorithm = thePlotLabelingAlgorithm;
}
@Override
public AbbreviatingPlotLabelingAlgorithm getPlotLabelingAlgorithm() {
return this.plotLabelingAlgorithm;
}
@Override
public void addData(String feedID, SortedMap<Long, Double> points) {
legendManager.setVisible(true);
dataManager.addData(feedID, points);
// TODO: This will also need to work separately for dependent/independent bounds
// boolean changed = false ;
// for (boolean maximal : new boolean[]{true, false}) {
// if ((maximal ? getNonTimeAxisSubsequentMaxSetting() : getNonTimeAxisSubsequentMinSetting())
// == NonTimeAxisSubsequentBoundsSetting.AUTO) {
// changed |= autoExpand(true, maximal);
// changed |= autoExpand(false, maximal);
// }
// }
// if (changed) {
// setupAxisBounds();
// }
}
private boolean autoExpand(boolean dependent, boolean maximal) {
double current = dependent ? (maximal ? getMaxDependent() : getMinDependent()) :
(maximal ? getMaxNonTime() : getMinNonTime());
double minimum = dataManager.getExtremum(
timeAxis.getStartAsLong(), timeAxis.getEndAsLong(), false, dependent);
double maximum = dataManager.getExtremum(
timeAxis.getStartAsLong(), timeAxis.getEndAsLong(), true, dependent);
double extremum = maximal ? maximum : minimum;
double padding = (maximal ? getNonTimeMaxPadding() : getNonTimeMinPadding()) *
(maximum - minimum);
if (maximal && extremum > current) {
if (dependent) setMaxDependent(extremum + padding);
else setMaxNonTime(extremum + padding);
} else if (!maximal && extremum < current) {
if (dependent) setMinDependent(extremum - padding);
else setMinNonTime(extremum - padding);
} else {
return false;
}
return true;
}
@Override
public void addData(String feed, long time, double value) {
SortedMap<Long, Double> points = new TreeMap<Long, Double>();
points.put(time, value);
addData(feed, points);
}
@Override
public void setTruncationPoint(double min) {
// TODO Auto-generated method stub
}
@Override
public void updateCompressionRatio() {
// TODO Auto-generated method stub
}
@Override
public PlotAbstraction getPlotAbstraction() {
return abstraction;
}
@Override
public LegendManager getLegendManager() {
return legendManager;
}
@Override
public AbstractPlotDataManager getPlotDataManager() {
return dataManager;
}
@Override
public AbstractPlotLocalControlsManager getLocalControlsManager() {
return localControls;
}
@Override
public PlotViewActionListener getPlotActionListener() {
return actionListener;
}
@Override
public PlotLimitManager getLimitManager() {
return null;
}
@Override
public AbstractPlotLine createPlotLine() {
ScatterXYPlotLine plotterLine =
new ScatterXYPlotLine(plotPanel.getXAxis(), plotPanel.getYAxis());
plotterLine.setForeground(Color.PINK);
SimpleXYDataset data = new SimpleXYDataset(plotterLine);
plotPanel.getContents().add(plotterLine);
return new PlotLineWrapper(plotterLine, data);
}
private static class PlotLineWrapper implements AbstractPlotLine {
private SimpleXYDataset data;
private ScatterXYPlotLine plotLine;
public PlotLineWrapper(ScatterXYPlotLine plotLine, SimpleXYDataset data) {
this.plotLine = plotLine;
this.data = data;
}
@Override
public void appendData(double independent, double dependent) {
data.add(independent, dependent);
}
@Override
public Color getColor() {
return plotLine.getForeground();
}
@Override
public Icon getIcon() {
return plotLine.getPointIcon();
}
@Override
public void setColor(Color c) {
plotLine.setForeground(c);
}
@Override
public void removeFirst(int count) {
data.removeFirst(Math.min(count, data.getPointCount()));
}
@Override
public void appendData(double[] independent, double[] dependent) {
for (int i = 0 ; i < Math.min(independent.length, dependent.length); i++) {
appendData(independent[i], dependent[i]);
}
}
@Override
public void prependData(double independent, double dependent) {
prependData(new double[]{independent}, new double[]{dependent});
}
@Override
public void prependData(double[] independent, double[] dependent) {
data.prepend(independent, 0, dependent, 0, Math.min(independent.length, dependent.length));
}
@Override
public void removeLast(int count) {
data.removeLast(count);
}
}
@Override
public void addDataSet(String dataSetName, Color plottingColor,
AbstractLegendEntry legend) {
addDataSet(dataSetName, plottingColor);
AbstractPlotDataSeries series = dataManager.getNamedDataSeries(dataSetName);
if (series != null) {
series.setLegendEntry(legend);
}
}
@Override
public void attachLocalControl(AbstractPlotLocalControl control) {
plotPanel.add(control, 0);
SpringLayout layout = (SpringLayout) plotPanel.getLayout();
for (AttachmentLocation location : control.getDesiredAttachmentLocations()) {
if (location != null) {
layout.putConstraint(location.getControlPlacement(), control,
location.getDistance(), location.getContentPlacement(), plotPanel.getContents());
}
}
PlotObserver o = control.getPlotObserver();
if (o != null) {
this.registerObservor(o);
}
localControls.addControl(control);
}
@Override
public Collection<AbstractAxis> getAxes() {
return Arrays.asList( (AbstractAxis) plotPanel.getXAxis(), (AbstractAxis) plotPanel.getYAxis(), timeAxis) ;
}
@Override
public void notifyObserversAxisChanged(AbstractAxis axis) {
for (PlotObserver o : this.observers) {
o.plotAxisChanged(this, axis);
}
}
@Override
public Collection<AbstractAxisBoundManager> getBoundManagers(AxisVisibleOrientation axis) {
return boundManagers.get(axis);
}
}