Java tutorial
/* * Debrief - the Open Source Maritime Analysis Application * http://debrief.info * * (C) 2000-2014, PlanetMayo Ltd * * This library is free software; you can redistribute it and/or * modify it under the terms of the Eclipse Public License v1.0 * (http://www.eclipse.org/legal/epl-v10.html) * * This library 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. */ package org.mwc.cmap.xyplot.views; import java.awt.Color; import java.awt.Shape; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.swt.SWT; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.IMemento; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.renderer.xy.XYDotRenderer; import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.jfree.experimental.chart.swt.ChartComposite; import org.mwc.cmap.xyplot.views.providers.ICrossSectionDatasetProvider; import MWC.GUI.Layers; import MWC.GUI.Shapes.LineShape; import MWC.GenericData.HiResDate; import MWC.GenericData.WorldDistance; import MWC.GenericData.WorldVector; public class CrossSectionViewer { private List<ISelectionChangedListener> _listeners = new ArrayList<ISelectionChangedListener>(); /** * the Swing control we insert the plot into */ private ChartComposite _chartFrame; private JFreeChart _chart; private ICrossSectionDatasetProvider _datasetProvider; private XYSeriesCollection _dataset = new XYSeriesCollection(); private XYLineAndShapeRenderer _snailRenderer = new XYLineAndShapeRenderer(); private XYDotRenderer _discreteRenderer = new XYDotRenderer(); /** * The current time we are looking at */ private HiResDate _currentTime = null; /** * the chart marker */ private static final Shape _markerShape = new Rectangle2D.Double(-2, -2, 2, 2); private static final int _markerSize = 4; /** * Number of ticks to show before and after min and max axis values. */ private static final int TICK_OFFSET = 1; private long _timePeriod = 0; private String _plotId; /** * Memento parameters */ private static interface PLOT_ATTRIBUTES { final String TIME_PERIOD = "Time_Period"; final String IS_SNAIL = "Is_Snail"; final String ID = "Plot_Id"; } protected CrossSectionViewer(final Composite parent) { _chartFrame = new ChartComposite(parent, SWT.NONE, null, true) { @Override public void mouseUp(MouseEvent event) { super.mouseUp(event); JFreeChart c = getChart(); if (c != null) { c.setNotify(true); // force redraw } } }; _chart = ChartFactory.createXYLineChart("Cross Section", // Title "Distance (km)", // X-Axis label "Elevation (m)", // Y-Axis label _dataset, // Dataset, PlotOrientation.VERTICAL, true, // Show legend, true, // tooltips true // urs ); // Fix the axises start at zero final ValueAxis yAxis = _chart.getXYPlot().getRangeAxis(); yAxis.setLowerBound(0); final ValueAxis xAxis = _chart.getXYPlot().getDomainAxis(); xAxis.setLowerBound(0); _chartFrame.setChart(_chart); _chartFrame.addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(DisposeEvent e) { clearPlot(); _chartFrame.removeDisposeListener(this); } }); } protected boolean isSnail() { return _timePeriod != 0; } public void newTime(final HiResDate newDTG) { _currentTime = newDTG; } public void clearPlot() { if (_chartFrame == null || _chartFrame.isDisposed()) { return; } _dataset.removeAllSeries(); _chart.getXYPlot().setDataset(_dataset); _chart.getXYPlot().clearAnnotations(); } public void fillPlot(final Layers theLayers, final LineShape line, final ICrossSectionDatasetProvider prov) { fillPlot(theLayers, line, prov, _timePeriod); } public void fillPlot(final Layers theLayers, final LineShape line, final ICrossSectionDatasetProvider prov, final long timePeriod) { if (theLayers != null && line != null) { _timePeriod = timePeriod; _datasetProvider = prov; _dataset.removeAllSeries(); if (_currentTime != null) { if (isSnail()) { final HiResDate startDTG = new HiResDate(_currentTime.getDate().getTime() - _timePeriod); _dataset = _datasetProvider.getDataset(line, theLayers, startDTG, _currentTime); final Map<Integer, Color> colors = _datasetProvider.getSeriesColors(); for (Integer seriesKey : colors.keySet()) { setSnailRenderer(seriesKey, colors.get(seriesKey)); } _chart.getXYPlot().setRenderer(_snailRenderer); } else { _dataset = _datasetProvider.getDataset(line, theLayers, _currentTime); final Map<Integer, Color> colors = _datasetProvider.getSeriesColors(); for (Integer seriesKey : colors.keySet()) { setDiscreteRenderer(seriesKey, colors.get(seriesKey)); } _chart.getXYPlot().setRenderer(_discreteRenderer); } } double maxY = 0; double minY = Integer.MAX_VALUE; for (int i = 0; i < _dataset.getSeriesCount(); i++) { final XYSeries series = _dataset.getSeries(_dataset.getSeriesKey(i)); final double y = series.getMaxY(); if (maxY < y) maxY = y; final double mY = series.getMinY(); if (minY > mY) minY = mY; } final ValueAxis yAxis = _chart.getXYPlot().getRangeAxis(); yAxis.setUpperBound(maxY + TICK_OFFSET); yAxis.setLowerBound(minY - TICK_OFFSET); final ValueAxis xAxis = _chart.getXYPlot().getDomainAxis(); xAxis.setUpperBound(getXAxisLength(line) + TICK_OFFSET); xAxis.setLowerBound(0); _chart.getXYPlot().setDataset(_dataset); } } private double getXAxisLength(LineShape line) { final WorldVector wv = line.getLine_Start().subtract(line.getLineEnd()); return new WorldDistance(wv).getValueIn(WorldDistance.KM); } private void setDiscreteRenderer(final int series, final Color paint) { _discreteRenderer.setSeriesShape(series, _markerShape); _discreteRenderer.setSeriesFillPaint(series, paint); _discreteRenderer.setSeriesPaint(series, paint); _discreteRenderer.setDotHeight(_markerSize); _discreteRenderer.setDotWidth(_markerSize); } private void setSnailRenderer(final int series, final Color paint) { _snailRenderer.setSeriesShape(series, _markerShape); _snailRenderer.setSeriesFillPaint(series, paint); _snailRenderer.setSeriesPaint(series, paint); _snailRenderer.setUseFillPaint(true); _snailRenderer.setSeriesShapesFilled(series, true); _snailRenderer.setSeriesShapesVisible(series, true); } public void addSelectionChangedListener(final ISelectionChangedListener listener) { if (!_listeners.contains(listener)) _listeners.add(listener); } public void removeSelectionChangedListener(final ISelectionChangedListener listener) { _listeners.remove(listener); } public void saveState(final IMemento memento) { memento.putString(PLOT_ATTRIBUTES.ID, _plotId); final boolean is_snail = isSnail(); memento.putBoolean(PLOT_ATTRIBUTES.IS_SNAIL, is_snail); memento.putFloat(PLOT_ATTRIBUTES.TIME_PERIOD, (float) _timePeriod); } public void restoreState(final IMemento memento) { _plotId = memento.getString(PLOT_ATTRIBUTES.ID); final Boolean is_snail = memento.getBoolean(PLOT_ATTRIBUTES.IS_SNAIL); if (is_snail) { _timePeriod = memento.getFloat(PLOT_ATTRIBUTES.TIME_PERIOD).longValue(); } else { _timePeriod = 0; } } protected long getPeriod() { return _timePeriod; } protected String getPlotId() { return _plotId; } protected void setPlotId(final String _plotId) { this._plotId = _plotId; } static public final class CrossSectionViewerTest extends junit.framework.TestCase { // TODO: test for null current time // TODO: test for time stepping // To write such test we'd need some Mock framework } }