Java tutorial
package desmoj.extensions.grafic.util; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics2D; import java.awt.font.FontRenderContext; import java.awt.font.LineMetrics; import java.awt.geom.AffineTransform; import java.text.DateFormat; import java.text.DecimalFormat; import java.text.NumberFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import java.util.concurrent.TimeUnit; import javax.swing.JComponent; import javax.swing.JPanel; import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartPanel; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.AxisLocation; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.CategoryLabelPositions; import org.jfree.chart.axis.DateAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.axis.TickUnitSource; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.labels.StandardCategoryItemLabelGenerator; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.category.BarRenderer; import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import org.jfree.data.category.CategoryDataset; import org.jfree.data.category.DefaultCategoryDataset; import org.jfree.data.xy.XYDataset; import org.jfree.ui.RectangleInsets; import desmoj.core.simulator.TimeInstant; import desmoj.core.simulator.TimeOperations; import desmoj.core.simulator.TimeSpan; import desmoj.core.statistic.Histogram; import desmoj.core.statistic.HistogramAccumulate; import desmoj.core.statistic.TimeSeries; /** * Class to plot DesmoJ histogram, histogramAccumulater and time-series data in jFreeChart Plotter. * When in DesmoJ dataset getShowTimeSpansInReport() is set, the data values are interpreted as * a timespan in a appropriate time unit. * The DesmoJ datasets are converted in jFreeChart Format. * Onscreen and offscreen plots are supported. * * See also PaintPanel and TimeSeriesDataSetAdapter * * @version DESMO-J, Ver. 2.3.5 copyright (c) 2013 * @author christian.mueller@th-wildau.de and goebel@informatik.uni-hamburg.de * modified at 4.12.2012 by christian.mueller@th-wildau.de * * Licensed under the Apache License, Version 2.0 (the "License"); you * may not use this file except in compliance with the License. You may * obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. * */ public class Plotter { public static final int TimeSeries_ScatterPlot = 0; public static final int TimeSeries_StepChart = 1; public static final int TimeSeries_LineChart = 2; private PaintPanel paintPanel; private boolean onScreen; private TimeZone timeZone; private Locale locale; private Date begin; /* begin of timeseries */ private Date end; /* end of timeseries */ private FontRenderContext frc; /** * Constructor to set the path of output directory and the size of created image. * Default are onScreen = false, locale = Locale.getDefault, timeZone = TimeZone.getdefault * @param path * @param size */ public Plotter(String path, Dimension size) { this.paintPanel = new PaintPanel(path, size); this.onScreen = false; this.locale = Locale.getDefault(); this.timeZone = TimeZone.getDefault(); this.begin = null; this.end = null; this.frc = new FontRenderContext(new AffineTransform(), true, true); } /** * Grafic is shown on screen, stored on file otherwise * @param onscreen */ public void setOnScreen(boolean onScreen) { this.onScreen = onScreen; } /** * set locale for both grafic axis * @param locale */ public void setLocale(Locale locale) { this.locale = locale; } /** * set timeZone for timeseries dateaxis * @param timeZone */ public void setTimeZone(TimeZone timeZone) { this.timeZone = timeZone; } /** * set date range for timeseries dateaxis. * When null its automaticly configured by observations. * @param begin * @param end */ public void setTimeRange(TimeInstant begin, TimeInstant end) { this.begin = null; if (begin != null) this.begin = new Date(new Double(begin.getTimeAsDouble(TimeUnit.MILLISECONDS)).longValue()); this.end = null; if (end != null) this.end = new Date(new Double(end.getTimeAsDouble(TimeUnit.MILLISECONDS)).longValue()); } /** * make a histogram plot with a desmoJ histogram dataset. * Deprecated, use setOnScreen(onScreen); makeHistogramPlot(histogram); * In the case histogram.getShowTimeSpansInReport() the data values are interpreted as * a timespan in a appropriate time unit. * @param histogram * @param onScreen */ @Deprecated public void makeHistogramPlot(Histogram histogram, boolean onscreen) { boolean onScreen = this.onScreen; this.setOnScreen(onscreen); this.makeHistogramPlot(histogram); this.setOnScreen(onScreen); } /** * make a histogram plot with a desmoJ histogram dataset. * In the case histogram.getShowTimeSpansInReport() the data values are interpreted as * a timespan in a appropriate time unit. * @param histogram */ public void makeHistogramPlot(Histogram histogram) { if (this.onScreen) { paintPanel.show(this.getHistogramPlot(histogram), histogram.getName()); } else { paintPanel.save(this.getHistogramPlot(histogram), histogram.getName()); } } /** * make a histogramAccumulate plot with a desmoJ histogram dataset. * In the case histogram.getShowTimeSpansInReport() the data values are interpreted as * a timespan in a appropriate time unit. * @param histogram */ public void makeHistogramAccumulatePlot(HistogramAccumulate histogram) { if (this.onScreen) { paintPanel.show(this.getHistogramAccumulatePlot(histogram), histogram.getName()); } else { paintPanel.save(this.getHistogramAccumulatePlot(histogram), histogram.getName()); } } /** * make a time-series plot with a desmoJ time-series dataset. * Deprecated, use setOnScreen(onScreen); makeTimeSeriesPlot(ts, Plotter.TimeSeries_ScatterPlot, true); * In the case histogram.getShowTimeSpansInReport() the data values are interpreted as * a timespan in a appropriate time unit. * @param ts DesmoJ time-series * @param onscreen Grafic is shown on screen, stored on file otherwise */ @Deprecated public void makeTimeSeriesPlot(TimeSeries ts, boolean onscreen) { boolean onScreen = this.onScreen; this.setOnScreen(onscreen); this.makeTimeSeriesPlot(ts, Plotter.TimeSeries_ScatterPlot, true); this.setOnScreen(onScreen); } /** * make a time-series plot with a desmoJ time-series dataset. * In the case ts.getShowTimeSpansInReport() the data values are interpreted as * a timespan in a appropriate time unit. * @param ts DesmoJ time-series * @param plotType possible Values: * Plotter.TimeSeries_ScatterPlot, * Plotter.TimeSeries_StepChart * Plotter.TimeSeries_LinePlot * @param multipleValues When multipleValues is set, multiple range values of a time value are allowed. * In the opposite case only the last range value of a time value is accepted. */ public void makeTimeSeriesPlot(TimeSeries ts, int plotType, boolean multipleValues) { if (this.onScreen) { paintPanel.show(this.getTimeSeriesPanel(ts, plotType, multipleValues), ts.getName()); } else { paintPanel.save(this.getTimeSeriesPanel(ts, plotType, multipleValues), ts.getName()); } } /** * Build a JPanel with a histogram plot of a desmoJ histogram dataset * In the case histogram.getShowTimeSpansInReport() the data values are interpreted as * a timespan in a appropriate time unit. * @param histogram desmoJ histogram dataset * @return */ private JPanel getHistogramPlot(Histogram histogram) { JFreeChart chart; NumberFormat formatter = NumberFormat.getInstance(locale); HistogramDataSetAdapter dataSet = new HistogramDataSetAdapter(histogram, locale); String title = histogram.getName(); if (histogram.getDescription() != null) title = histogram.getDescription(); String xLabel = dataSet.getCategoryAxisLabel(); String yLabel = dataSet.getObservationAxisLabel(); chart = ChartFactory.createBarChart(title, xLabel, yLabel, dataSet, PlotOrientation.VERTICAL, false, true, false); chart.setBackgroundPaint(Color.white); CategoryPlot categoryplot = (CategoryPlot) chart.getPlot(); categoryplot.setBackgroundPaint(Color.lightGray); categoryplot.setRangeGridlinePaint(Color.white); categoryplot.setRangeAxisLocation(AxisLocation.BOTTOM_OR_LEFT); BarRenderer barrenderer = (BarRenderer) categoryplot.getRenderer(); barrenderer.setBaseItemLabelsVisible(true); StandardCategoryItemLabelGenerator generator = new StandardCategoryItemLabelGenerator("{2}", formatter); barrenderer.setBaseItemLabelGenerator(generator); CategoryAxis categoryaxis = categoryplot.getDomainAxis(); //categoryaxis.setCategoryLabelPositions(CategoryLabelPositions.DOWN_45); categoryaxis.setMaximumCategoryLabelLines(4); NumberAxis numberaxis = (NumberAxis) categoryplot.getRangeAxis(); numberaxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits()); //numberaxis.setUpperMargin(0.1D); numberaxis.setNumberFormatOverride(formatter); return new ChartPanel(chart); } /** * Build a JPanel with a histogram plot of a desmoJ histogramAccumulate dataset * In the case histogram.getShowTimeSpansInReport() the data values are interpreted as * a timespan in a appropriate time unit. * @param histogram desmoJ histogramAccumulate dataset * @return */ private JPanel getHistogramAccumulatePlot(HistogramAccumulate histogram) { JFreeChart chart; NumberFormat formatter = NumberFormat.getInstance(locale); HistogramDataSetAdapter dataSet = new HistogramDataSetAdapter(histogram, locale); String title = histogram.getName(); if (histogram.getDescription() != null) title = histogram.getDescription(); String xLabel = dataSet.getCategoryAxisLabel(); String yLabel = dataSet.getObservationAxisLabel(); chart = ChartFactory.createBarChart(title, xLabel, yLabel, dataSet, PlotOrientation.VERTICAL, false, true, false); chart.setBackgroundPaint(Color.white); CategoryPlot categoryplot = (CategoryPlot) chart.getPlot(); categoryplot.setBackgroundPaint(Color.lightGray); categoryplot.setRangeGridlinePaint(Color.white); categoryplot.setRangeAxisLocation(AxisLocation.BOTTOM_OR_LEFT); BarRenderer barrenderer = (BarRenderer) categoryplot.getRenderer(); barrenderer.setBaseItemLabelsVisible(true); StandardCategoryItemLabelGenerator generator = new StandardCategoryItemLabelGenerator("{2}", formatter); barrenderer.setBaseItemLabelGenerator(generator); CategoryAxis categoryaxis = categoryplot.getDomainAxis(); categoryaxis.setMaximumCategoryLabelLines(4); NumberAxis numberaxis = (NumberAxis) categoryplot.getRangeAxis(); numberaxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits()); numberaxis.setNumberFormatOverride(formatter); return new ChartPanel(chart); } /** * Build a JPanel with plotType of a DesmoJ time-series dataset. * When allowMultipleValues is set, multiple range values of a time value are allowed. * In the opposite Case only the last range value of a time value is accepted. * In the case ts.getShowTimeSpansInReport() the data values are interpreted as * a timespan in a appropriate time unit. * @param ts DesmoJ time-series dataset * @param plotType possible Values: * Plotter.TimeSeries_ScatterPlot, * Plotter.TimeSeries_StepChart * Plotter.TimeSeries_LinePlot * @param allowMultipleValues * @return */ private JPanel getTimeSeriesPanel(TimeSeries ts, int plotType, boolean allowMultipleValues) { JFreeChart chart; TimeSeriesDataSetAdapter dataset = new TimeSeriesDataSetAdapter(ts, allowMultipleValues); switch (plotType) { case Plotter.TimeSeries_LineChart: chart = ChartFactory.createXYLineChart(ts.getName(), "Time", "Observation", dataset, PlotOrientation.VERTICAL, false, false, false); break; case Plotter.TimeSeries_ScatterPlot: chart = ChartFactory.createScatterPlot(ts.getName(), "Time", "Observation", dataset, PlotOrientation.VERTICAL, false, false, false); break; case Plotter.TimeSeries_StepChart: chart = ChartFactory.createXYStepChart(ts.getName(), "Time", "Observation", dataset, PlotOrientation.VERTICAL, false, false, false); break; default: chart = ChartFactory.createScatterPlot(ts.getName(), "Time", "Observation", dataset, PlotOrientation.VERTICAL, false, false, false); break; } if (ts.getDescription() != null) chart.setTitle(ts.getDescription()); XYPlot xyplot = (XYPlot) chart.getPlot(); xyplot.setNoDataMessage("NO DATA"); if (ts.getShowTimeSpansInReport() && !dataset.isValid()) xyplot.setNoDataMessage("NO VALID TIMESPANS"); xyplot.setDomainZeroBaselineVisible(false); xyplot.setRangeZeroBaselineVisible(false); DateAxis dateAxis = new DateAxis(); xyplot.setDomainAxis(dateAxis); this.configureDomainAxis(dateAxis); String numberLabel; if (!dataset.isValid()) numberLabel = "Unit: invalid"; else if (ts.getShowTimeSpansInReport()) numberLabel = "Unit: timespan [" + dataset.getRangeTimeUnit().name() + "]"; else if (ts.getUnit() != null) numberLabel = "Unit: [" + ts.getUnit() + "]"; else numberLabel = "Unit: unknown"; NumberAxis numberAxis = new NumberAxis(); xyplot.setRangeAxis(numberAxis); this.configureRangeAxis(numberAxis, numberLabel); XYLineAndShapeRenderer xylineandshaperenderer = (XYLineAndShapeRenderer) xyplot.getRenderer(); xylineandshaperenderer.setSeriesOutlinePaint(0, Color.black); xylineandshaperenderer.setUseOutlinePaint(true); ChartPanel panel = new ChartPanel(chart); panel.setVerticalAxisTrace(false); panel.setHorizontalAxisTrace(false); panel.setPopupMenu(null); panel.setDomainZoomable(false); panel.setRangeZoomable(false); return panel; } /** * configure domainAxis (label, timeZone, locale, tick labels) * of time-series chart * @param dateAxis */ private void configureDomainAxis(DateAxis dateAxis) { if (this.begin != null) dateAxis.setMinimumDate(this.begin); if (this.end != null) dateAxis.setMaximumDate(this.end); dateAxis.setTimeZone(this.timeZone); Date dateMin = dateAxis.getMinimumDate(); Date dateMax = dateAxis.getMaximumDate(); //DateFormat formatter = new SimpleDateFormat("d.MM.yyyy HH:mm:ss.SSS");; DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.MEDIUM, locale); String label = "Time: " + formatter.format(dateMin) + " .. " + formatter.format(dateMax); formatter = new SimpleDateFormat("z"); label += " " + formatter.format(dateMax); dateAxis.setLabel(label); TimeInstant max = new TimeInstant(dateMax); TimeInstant min = new TimeInstant(dateMin); TimeSpan diff = TimeOperations.diff(max, min); if (TimeSpan.isLongerOrEqual(diff, new TimeSpan(1, TimeUnit.DAYS))) formatter = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, locale); else if (TimeSpan.isLongerOrEqual(diff, new TimeSpan(1, TimeUnit.HOURS))) formatter = DateFormat.getTimeInstance(DateFormat.SHORT, locale); else if (TimeSpan.isLongerOrEqual(diff, new TimeSpan(1, TimeUnit.MINUTES))) formatter = DateFormat.getTimeInstance(DateFormat.MEDIUM, locale); else formatter = new SimpleDateFormat("HH:mm:ss.SSS"); dateAxis.setDateFormatOverride(formatter); dateAxis.setVerticalTickLabels(true); } /** * configure range axis ( lowerBound, upperBound, label, format ticks) * of time-series chart * @param numberAxis * @param label */ private void configureRangeAxis(NumberAxis numberAxis, String label) { double min = numberAxis.getLowerBound(); double max = numberAxis.getUpperBound(); Double delta = 0.01 * (max - min); numberAxis.setLowerBound(min - delta); numberAxis.setUpperBound(max + delta); numberAxis.setLabel(label); // format Ticks double fontHeight = numberAxis.getTickLabelFont().getLineMetrics("X", this.frc).getHeight(); double maxTicks = this.paintPanel.getSize().height / fontHeight; int digits = Math.max(0, (int) -Math.floor(Math.log10((max - min) / maxTicks))); //System.out.println(fontHeight+" "+digits+" "+Math.log10((max - min)/ maxTicks)); NumberFormat formatter = NumberFormat.getNumberInstance(this.locale); formatter.setMinimumFractionDigits(digits); formatter.setMaximumFractionDigits(digits); formatter.setGroupingUsed(true); numberAxis.setNumberFormatOverride(formatter); } }