Java tutorial
/********************************************************************************************* * * * 'ChartLayerStatement.java', in plugin 'msi.gama.core', is part of the source code of the * GAMA modeling and simulation platform. * (c) 2007-2014 UMI 209 UMMISCO IRD/UPMC & Partners * * Visit https://code.google.com/p/gama-platform/ for license information and developers contact. * * **********************************************************************************************/ package msi.gama.outputs.layers; import java.awt.*; import java.io.*; import java.util.*; import java.util.List; import msi.gama.common.interfaces.IKeyword; import msi.gama.common.util.FileUtils; import msi.gama.kernel.experiment.BatchAgent; import msi.gama.kernel.simulation.SimulationAgent; import msi.gama.metamodel.shape.GamaPoint; import msi.gama.outputs.layers.ChartDataListStatement.ChartDataList; import msi.gama.outputs.layers.ChartDataStatement.ChartData; import msi.gama.precompiler.GamlAnnotations.doc; import msi.gama.precompiler.GamlAnnotations.example; import msi.gama.precompiler.GamlAnnotations.facet; import msi.gama.precompiler.GamlAnnotations.facets; import msi.gama.precompiler.GamlAnnotations.inside; import msi.gama.precompiler.GamlAnnotations.symbol; import msi.gama.precompiler.GamlAnnotations.usage; import msi.gama.precompiler.*; import msi.gama.runtime.*; import msi.gama.runtime.exceptions.GamaRuntimeException; import msi.gama.util.*; import msi.gaml.compilation.ISymbol; import msi.gaml.descriptions.IDescription; import msi.gaml.expressions.IExpression; import msi.gaml.factories.DescriptionFactory; import msi.gaml.operators.*; import msi.gaml.operators.Random; import msi.gaml.statements.AbstractStatementSequence; import msi.gaml.types.*; import org.jfree.chart.*; import org.jfree.chart.axis.*; import org.jfree.chart.labels.StandardPieSectionLabelGenerator; import org.jfree.chart.plot.*; import org.jfree.chart.renderer.AbstractRenderer; import org.jfree.chart.renderer.category.*; import org.jfree.chart.renderer.xy.*; import org.jfree.chart.title.LegendTitle; import org.jfree.data.category.DefaultCategoryDataset; import org.jfree.data.general.*; import org.jfree.data.statistics.*; import org.jfree.data.xy.*; import org.jfree.ui.RectangleInsets; /** * Written by drogoul Modified on 9 nov. 2009 * * @todo Description * */ @symbol(name = IKeyword.CHART, kind = ISymbolKind.LAYER, with_sequence = true) @inside(symbols = IKeyword.DISPLAY) @facets(value = { /* @facet(name = ISymbol.VALUE, type = TypeManager.STRING, optional = true), */ @facet(name = ChartLayerStatement.XRANGE, type = { IType.FLOAT, IType.INT, IType.POINT }, optional = true, doc = @doc("range of the x-axis. Can be a number (which will set the axis total range) or a point (which will set the min and max of the axis).")), @facet(name = ChartLayerStatement.YRANGE, type = { IType.FLOAT, IType.INT, IType.POINT }, optional = true, doc = @doc("range of the y-axis. Can be a number (which will set the axis total range) or a point (which will set the min and max of the axis).")), @facet(name = IKeyword.POSITION, type = IType.POINT, optional = true, doc = @doc("position of the upper-left corner of the layer. Note that if coordinates are in [0,1[, the position is relative to the size of the environment (e.g. {0.5,0.5} refers to the middle of the display) whereas it is absolute when coordinates are greter than 1. The position can only be a 3D point {0.5, 0.5, 0.5}, the last coordinate specifying the elevation of the layer.")), @facet(name = IKeyword.SIZE, type = IType.POINT, optional = true, doc = @doc("the layer resize factor: {1,1} refers to the original size whereas {0.5,0.5} divides by 2 the height and the width of the layer. In case of a 3D layer, a 3D point can be used (note that {1,1} is equivalent to {1,1,0}, so a resize of a layer containing 3D objects with a 2D points will remove the elevation)")), @facet(name = IKeyword.BACKGROUND, type = IType.COLOR, optional = true, doc = @doc("the background color")), @facet(name = IKeyword.TIMEXSERIES, type = IType.LIST, optional = true, doc = @doc("for series charts, change the default time serie (simulation cycle) for an other value.")), @facet(name = IKeyword.AXES, type = IType.COLOR, optional = true, doc = @doc("the axis color")), @facet(name = IKeyword.TYPE, type = IType.ID, values = { IKeyword.XY, IKeyword.SCATTER, IKeyword.HISTOGRAM, IKeyword.SERIES, IKeyword.PIE, IKeyword.BOX_WHISKER }, optional = true, doc = @doc("the type of chart. It could be histogram, series, xy, pie or box whisker. The difference between series and xy is that the former adds an implicit x-axis that refers to the numbers of cycles, while the latter considers the first declaration of data to be its x-axis.")), @facet(name = IKeyword.STYLE, type = IType.ID, values = { IKeyword.EXPLODED, IKeyword.THREE_D, IKeyword.STACK, IKeyword.BAR }, optional = true), @facet(name = IKeyword.TRANSPARENCY, type = IType.FLOAT, optional = true, doc = @doc("the style of the chart")), @facet(name = IKeyword.GAP, type = IType.FLOAT, optional = true), @facet(name = ChartLayerStatement.YTICKUNIT, type = IType.FLOAT, optional = true, doc = @doc("the tick unit for the x-axis (distance between vertical lines and values bellow the axis).")), @facet(name = ChartLayerStatement.XTICKUNIT, type = IType.FLOAT, optional = true, doc = @doc("the tick unit for the y-axis (distance between horyzontal lines and values on the left of the axis).")), @facet(name = IKeyword.NAME, type = IType.LABEL, optional = false, doc = @doc("the identifier of the chart layer")), @facet(name = IKeyword.COLOR, type = IType.COLOR, optional = true), @facet(name = ChartLayerStatement.TICKFONTFACE, type = IType.STRING, optional = true), @facet(name = ChartLayerStatement.TICKFONTSIZE, type = IType.INT, optional = true), @facet(name = ChartLayerStatement.TICKFONTSTYLE, type = IType.ID, values = { "plain", "bold", "italic" }, optional = true, doc = @doc("the style used to display ticks")), @facet(name = ChartLayerStatement.LABELFONTFACE, type = IType.STRING, optional = true), @facet(name = ChartLayerStatement.LABELFONTSIZE, type = IType.INT, optional = true), @facet(name = ChartLayerStatement.LABELFONTSTYLE, type = IType.ID, values = { "plain", "bold", "italic" }, optional = true, doc = @doc("the style used to display labels")), @facet(name = ChartLayerStatement.LEGENDFONTFACE, type = IType.STRING, optional = true), @facet(name = ChartLayerStatement.LEGENDFONTSIZE, type = IType.INT, optional = true), @facet(name = ChartLayerStatement.LEGENDFONTSTYLE, type = IType.ID, values = { "plain", "bold", "italic" }, optional = true, doc = @doc("the style used to display legend")), @facet(name = ChartLayerStatement.TITLEFONTFACE, type = IType.STRING, optional = true), @facet(name = ChartLayerStatement.TITLEFONTSIZE, type = IType.INT, optional = true), @facet(name = ChartLayerStatement.TITLEFONTSTYLE, type = IType.ID, values = { "plain", "bold", "italic" }, optional = true, doc = @doc("the style used to display titles")), }, omissible = IKeyword.NAME) @doc(value = "`" + IKeyword.CHART + "` allows modeler to display a chart: this enables to display specific values of the model at each iteration. GAMA can display various chart types: time series (series), pie charts (pie) and histograms (histogram).", usages = { @usage(value = "The general syntax is:", examples = { @example(value = "display chart_display {", isExecutable = false), @example(value = " chart \"chart name\" type: series [additional options] {", isExecutable = false), @example(value = " [Set of data, datalists statements]", isExecutable = false), @example(value = " }", isExecutable = false), @example(value = "}", isExecutable = false) }) }, see = { IKeyword.DISPLAY, IKeyword.AGENTS, IKeyword.EVENT, "graphics", IKeyword.GRID_POPULATION, IKeyword.IMAGE, IKeyword.OVERLAY, IKeyword.QUADTREE, IKeyword.POPULATION, IKeyword.TEXT }) public class ChartLayerStatement extends AbstractLayerStatement { public static final String XRANGE = "x_range"; public static final String YRANGE = "y_range"; public static final String YTICKUNIT = "y_tick_unit"; public static final String XTICKUNIT = "x_tick_unit"; public static final String TICKFONTFACE = "tick_font"; public static final String TICKFONTSIZE = "tick_font_size"; public static final String TICKFONTSTYLE = "tick_font_style"; public static final String LABELFONTFACE = "label_font"; public static final String LABELFONTSIZE = "label_font_size"; public static final String LABELFONTSTYLE = "label_font_style"; public static final String LEGENDFONTFACE = "legend_font"; public static final String LEGENDFONTSIZE = "legend_font_size"; public static final String LEGENDFONTSTYLE = "legend_font_style"; public static final String TITLEFONTFACE = "title_font"; public static final String TITLEFONTSIZE = "title_font_size"; public static final String TITLEFONTSTYLE = "title_font_style"; public class DataDeclarationSequence extends AbstractStatementSequence { public DataDeclarationSequence(final IDescription desc) { super(desc); } // We create the variable in which the datas will be accumulated @Override public void enterScope(final IScope scope) { super.enterScope(scope); scope.addVarWithValue(ChartDataStatement.DATAS, new ArrayList()); scope.addVarWithValue(ChartDataListStatement.DATALISTS, new ArrayList()); } // We save the datas once the computation is finished @Override public void leaveScope(final IScope scope) { datas = (List<ChartData>) scope.getVarValue(ChartDataStatement.DATAS); datalists = (List<ChartDataList>) scope.getVarValue(ChartDataListStatement.DATALISTS); super.leaveScope(scope); } } private static final int SERIES_CHART = 0; private static final int HISTOGRAM_CHART = 1; private static final int PIE_CHART = 2; private static final int XY_CHART = 3; private static final int BOX_WHISKER_CHART = 4; private static final int SCATTER_CHART = 5; private int type = SERIES_CHART; private String style = IKeyword.DEFAULT; private JFreeChart chart = null; private StringBuilder history; private static String chartFolder = "charts"; private String tickFontFace = Font.SANS_SERIF; private int tickFontSize = 10; private int tickFontStyle = Font.PLAIN; private String labelFontFace = Font.SANS_SERIF; private int labelFontSize = 12; private int labelFontStyle = Font.BOLD; private String legendFontFace = Font.SANS_SERIF; private int legendFontSize = 10; private int legendFontStyle = Font.ITALIC; private String titleFontFace = Font.SERIF; private int titleFontSize = 14; private int titleFontStyle = Font.BOLD; private GamaColor backgroundColor = null, axesColor = null; private final Map<String, Integer> expressions_index = new HashMap(); private Dataset dataset; private boolean exploded; static String xAxisName = "'time'"; List<ChartData> datas; List<ChartData> datasfromlists; List<ChartDataList> datalists; final Map<String, Double> lastValues; Long lastComputeCycle; ChartDataStatement timeSeriesXData = null; DataDeclarationSequence dataDeclaration = new DataDeclarationSequence(null); public JFreeChart getChart() { return chart; } public ChartLayerStatement(/* final ISymbol context, */final IDescription desc) throws GamaRuntimeException { super(desc); axesColor = new GamaColor(Color.black); lastValues = new LinkedHashMap(); lastComputeCycle = 0l; } Font getLabelFont() { return new Font(labelFontFace, labelFontStyle, labelFontSize); } Font getTickFont() { return new Font(tickFontFace, tickFontStyle, tickFontSize); } Font getLegendFont() { return new Font(legendFontFace, legendFontStyle, legendFontSize); } Font getTitleFont() { return new Font(titleFontFace, titleFontStyle, titleFontSize); } @Override public void setChildren(final List<? extends ISymbol> commands) { dataDeclaration.setChildren(commands); } void createSeries(final IScope scope, final boolean isTimeSeries) throws GamaRuntimeException { final XYPlot plot = (XYPlot) chart.getPlot(); final NumberAxis domainAxis = (NumberAxis) plot.getDomainAxis(); domainAxis.setTickLabelFont(getTickFont()); domainAxis.setLabelFont(getLabelFont()); if (isTimeSeries) { domainAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits()); if (timeSeriesXData == null) { timeSeriesXData = (ChartDataStatement) DescriptionFactory.create(IKeyword.DATA, description, IKeyword.LEGEND, xAxisName, IKeyword.VALUE, SimulationAgent.CYCLE).compile(); if (getFacet(IKeyword.TIMEXSERIES) != null) { timeSeriesXData.getDescription().getFacets().get(IKeyword.VALUE) .setExpression(getFacet(IKeyword.TIMEXSERIES)); } } // FIXME: datas can NOT contain timeSeriesXData (a ChartDataStatement and not a ChartData) if (!datas.contains(timeSeriesXData)) { datas.add(0, timeSeriesXData.createData(scope)); } } IExpression expr = getFacet(XRANGE); IExpression expr2 = getFacet(XTICKUNIT); if (expr != null) { Object range = expr.value(scope); // Double range = Cast.asFloat(scope, expr.value(scope)); if (range instanceof Number) { double r = ((Number) range).doubleValue(); if (r > 0) { domainAxis.setFixedAutoRange(r); domainAxis.setAutoRangeMinimumSize(r); } domainAxis.setAutoRangeIncludesZero(false); } else if (range instanceof GamaPoint) { domainAxis.setRange(((GamaPoint) range).getX(), ((GamaPoint) range).getY()); } } if (expr2 != null) { Object range = expr2.value(scope); // Double range = Cast.asFloat(scope, expr.value(scope)); if (range instanceof Number) { double r = ((Number) range).doubleValue(); if (r > 0) { domainAxis.setTickUnit(new NumberTickUnit(r)); } } } if (datas.size() > 0) { domainAxis.setLabel(datas.get(0).getName()); } final NumberAxis yAxis = (NumberAxis) plot.getRangeAxis(); yAxis.setTickLabelFont(getTickFont()); yAxis.setLabelFont(getLabelFont()); expr = getFacet(YRANGE); expr2 = getFacet(YTICKUNIT); if (expr != null) { Object range = expr.value(scope); // Double range = Cast.asFloat(scope, expr.value(scope)); if (range instanceof Number) { double r = ((Number) range).doubleValue(); if (r > 0) { yAxis.setFixedAutoRange(r); yAxis.setAutoRangeMinimumSize(r); } yAxis.setAutoRangeIncludesZero(false); } else if (range instanceof GamaPoint) { yAxis.setRange(((GamaPoint) range).getX(), ((GamaPoint) range).getY()); } } if (expr2 != null) { Object range = expr2.value(scope); // Double range = Cast.asFloat(scope, expr.value(scope)); if (range instanceof Number) { double r = ((Number) range).doubleValue(); if (r > 0) { yAxis.setTickUnit(new NumberTickUnit(r)); } } } if (datas.size() == 2) { yAxis.setLabel(datas.get(1).getName()); chart.removeLegend(); } final LegendTitle ll = chart.getLegend(); if (ll != null) { ll.setItemFont(getLegendFont()); } for (int i = 0; i < datas.size(); i++) { ChartData e = datas.get(i); final String legend = e.getName(); if (i != 0 | !isTimeSeries) { // the first data is the domain XYDataset data = plot.getDataset(i); XYSeries serie = new XYSeries(0, false, false); if (type == SERIES_CHART || type == XY_CHART) { dataset = new DefaultTableXYDataset(); // final XYSeries nserie = new XYSeries(serie.getKey(), false, false); final XYSeries nserie = new XYSeries(e.getName(), false, false); ((DefaultTableXYDataset) dataset).addSeries(nserie); } if (type == SCATTER_CHART) { dataset = new XYSeriesCollection(); final XYSeries nserie = new XYSeries(e.getName(), false, true); // final XYSeries nserie = new XYSeries(serie.getKey(), false, true); ((XYSeriesCollection) dataset).addSeries(nserie); } // dataset = new DefaultTableXYDataset(); // final XYSeries serie = new XYSeries(legend, false, false); // final XYSeries serie = new XYSeries(legend, false, true); // ((DefaultTableXYDataset) dataset).addSeries(serie); expressions_index.put(legend, i); plot.setRenderer(i, (XYItemRenderer) e.getRenderer(), false); // final Color c = e.getColor(); // ((XYLineAndShapeRenderer) plot.getRenderer(i)).setSeriesPaint(0, c); // TODO Control this with a facet // ((XYLineAndShapeRenderer) plot.getRenderer(i)).setBaseShapesFilled(false); // TODO Control this with a facet // ((XYLineAndShapeRenderer) plot.getRenderer(i)).setSeriesShapesVisible(0, false); // if (type==SERIES_CHART) // plot.setDataset(i-1, (DefaultTableXYDataset) dataset); // else plot.setDataset(i, (XYDataset) dataset); } history.append(legend); history.append(','); } if (history.length() > 0) { history.deleteCharAt(history.length() - 1); } history.append(Strings.LN); } /** * create dataset for box_whisker chart * @return A sample dataset. */ private BoxAndWhiskerCategoryDataset createWhisker(final IScope scope) { final CategoryPlot plot = (CategoryPlot) chart.getPlot(); // final int seriesCount = 1; final int categoryCount = 3; final int entityCount = 2; final DefaultBoxAndWhiskerCategoryDataset dataset = new DefaultBoxAndWhiskerCategoryDataset(); for (int i = 0; i < datas.size(); i++) { // ChartData e = datas.get(i); for (int j = 0; j < categoryCount; j++) { final List list = new ArrayList(); // add some values... for (int k = 0; k < entityCount; k++) { // list.add(new Double(k*2)); // list.add(new Double(k*3)); final double value1 = 10.0 + Math.random() * 3; list.add(new Double(value1)); final double value2 = 11.25 + Math.random(); // concentrate values in the middle list.add(new Double(value2)); } dataset.add(list, "Series " + i, " Type " + j); history.append("Series " + i); history.append(','); } } history.deleteCharAt(history.length() - 1); history.append(Strings.LN); plot.setDataset(dataset); chart.removeLegend(); final CategoryAxis axis = plot.getDomainAxis(); axis.setTickLabelFont(getTickFont()); axis.setLabelFont(getLabelFont()); // ((BarRenderer3D) plot.getRenderer()).setItemMargin(0.1); axis.setCategoryMargin(0.1); axis.setUpperMargin(0.05); axis.setLowerMargin(0.05); return dataset; } private void createData(final IScope scope) throws GamaRuntimeException { // Normally initialize the datas dataDeclaration.executeOn(scope); switch (type) { case SERIES_CHART: { createSeries(scope, true); break; } case PIE_CHART: { createSlices(scope); break; } case BOX_WHISKER_CHART: { createWhisker(scope); break; } case HISTOGRAM_CHART: { createBars(scope); break; } case SCATTER_CHART: { createSeries(scope, false); break; } case XY_CHART: createSeries(scope, false); break; } } private void createSlices(final IScope scope) throws GamaRuntimeException { int i = 0; dataset = new DefaultPieDataset(); final PiePlot plot = (PiePlot) chart.getPlot(); for (final ChartData e : datas) { final String legend = e.getName(); ((DefaultPieDataset) dataset).insertValue(i++, legend, null); history.append(legend); history.append(','); } if (history.length() > 0) { history.deleteCharAt(history.length() - 1); } history.append(Strings.LN); plot.setDataset((DefaultPieDataset) dataset); i = 0; for (final ChartData e : datas) { plot.setSectionPaint(i++, e.getColor()); } plot.setLabelGenerator(new StandardPieSectionLabelGenerator("{0} = {1} ({2})")); if (exploded) { for (final Object c : ((DefaultPieDataset) dataset).getKeys()) { plot.setExplodePercent((Comparable) c, 0.20); } } plot.setSectionOutlinesVisible(false); plot.setLabelFont(getLabelFont()); plot.setNoDataMessage("No data available yet"); plot.setCircular(true); plot.setLabelGap(0.02); plot.setInteriorGap(0); } class CustomRenderer extends BarRenderer { public CustomRenderer() { } @Override public Paint getItemPaint(final int row, final int column) { return datas.get(column).getColor(); } } private void createBars(final IScope scope) { final CategoryPlot plot = (CategoryPlot) chart.getPlot(); BarRenderer renderer = new CustomRenderer(); plot.setRenderer(renderer); dataset = new DefaultCategoryDataset(); int i = 0; for (final ChartData e : datas) { // String legend = e.getName(); // ((DefaultCategoryDataset) dataset).setValue(0d, new Integer(0), legend/* , legend */); final String legend = e.getName(); if (!CategoryItemRenderer.class.isInstance(e.getRenderer())) { e.renderer = new CustomRenderer(); } plot.setRenderer(i, (CategoryItemRenderer) e.getRenderer(), false); final Color c = e.getColor(); plot.getRenderer(i).setSeriesPaint(0, c); // plot.setDataset(i, (DefaultCategoryDataset) dataset); i++; history.append(legend); history.append(','); } if (history.length() > 0) { history.deleteCharAt(history.length() - 1); } history.append(Strings.LN); plot.setDataset((DefaultCategoryDataset) dataset); chart.removeLegend(); final NumberAxis yAxis = (NumberAxis) plot.getRangeAxis(); yAxis.setTickLabelFont(getTickFont()); yAxis.setLabelFont(getLabelFont()); IExpression expr = getFacet(YRANGE); IExpression expr2 = getFacet(YTICKUNIT); if (expr != null) { Object range = expr.value(scope); // Double range = Cast.asFloat(scope, expr.value(scope)); if (range instanceof Number) { double r = ((Number) range).doubleValue(); if (r > 0) { yAxis.setFixedAutoRange(r); yAxis.setAutoRangeMinimumSize(r); } // yAxis.setAutoRangeIncludesZero(false); } else if (range instanceof GamaPoint) { yAxis.setRange(((GamaPoint) range).getX(), ((GamaPoint) range).getY()); } } if (expr2 != null) { Object range = expr2.value(scope); // Double range = Cast.asFloat(scope, expr.value(scope)); if (range instanceof Number) { double r = ((Number) range).doubleValue(); if (r > 0) { yAxis.setTickUnit(new NumberTickUnit(r)); } } } final CategoryAxis axis = plot.getDomainAxis(); Double gap = Cast.asFloat(scope, getFacetValue(scope, IKeyword.GAP, 0.01)); // ((BarRenderer) plot.getRenderer()).setItemMargin(gap); renderer.setMaximumBarWidth(1 - gap); axis.setCategoryMargin(gap); axis.setUpperMargin(gap); axis.setLowerMargin(gap); } private void createChart(final IScope scope) { switch (type) { case SERIES_CHART: { chart = ChartFactory.createXYLineChart(getName(), "time", "", null, PlotOrientation.VERTICAL, true, false, false); break; } case PIE_CHART: { if (style.equals(IKeyword.THREE_D)) { chart = ChartFactory.createPieChart3D(getName(), null, false, true, false); } else if (style.equals(IKeyword.RING)) { chart = ChartFactory.createRingChart(getName(), null, false, true, false); } else if (style.equals(IKeyword.EXPLODED)) { chart = ChartFactory.createPieChart(getName(), null, false, true, false); exploded = true; } else { chart = ChartFactory.createPieChart(getName(), null, false, true, false); } break; } case HISTOGRAM_CHART: { if (style.equals(IKeyword.THREE_D)) { chart = ChartFactory.createBarChart3D(getName(), null, null, null, PlotOrientation.VERTICAL, true, true, false); } else if (style.equals(IKeyword.STACK)) { chart = ChartFactory.createStackedBarChart(getName(), null, null, null, PlotOrientation.VERTICAL, true, true, false); } else { chart = ChartFactory.createBarChart(getName(), null, null, null, PlotOrientation.VERTICAL, true, true, false); } break; } case XY_CHART: chart = ChartFactory.createXYLineChart(getName(), "", "", null, PlotOrientation.VERTICAL, true, false, false); break; case SCATTER_CHART: chart = ChartFactory.createXYLineChart(getName(), "", "", null, PlotOrientation.VERTICAL, true, false, false); break; case BOX_WHISKER_CHART: { chart = ChartFactory.createBoxAndWhiskerChart(getName(), "Time", "Value", (BoxAndWhiskerCategoryDataset) dataset, true); chart.setBackgroundPaint(new Color(249, 231, 236)); break; } } Plot plot = chart.getPlot(); chart.getTitle().setFont(getTitleFont()); if (backgroundColor == null) { plot.setBackgroundPaint(null); chart.setBackgroundPaint(null); chart.setBorderPaint(null); if (chart.getLegend() != null) { chart.getLegend().setBackgroundPaint(null); } } else { Color bg = backgroundColor; chart.setBackgroundPaint(bg); plot.setBackgroundPaint(bg); chart.setBorderPaint(bg); if (chart.getLegend() != null) { chart.getLegend().setBackgroundPaint(bg); } } // chart.getLegend().setItemPaint(axesColor); // chart.getLegend().setBackgroundPaint(null); if (plot instanceof CategoryPlot) { final CategoryPlot pp = (CategoryPlot) chart.getPlot(); pp.setDomainGridlinePaint(axesColor); pp.setRangeGridlinePaint(axesColor); // plot.setAxisOffset(new RectangleInsets(5.0, 5.0, 5.0, 5.0)); // plot.setDomainCrosshairVisible(true); pp.setRangeCrosshairVisible(true); } else if (plot instanceof XYPlot) { final XYPlot pp = (XYPlot) chart.getPlot(); pp.setDomainGridlinePaint(axesColor); pp.setRangeGridlinePaint(axesColor); pp.setDomainCrosshairPaint(axesColor); pp.setRangeCrosshairPaint(axesColor); pp.setAxisOffset(new RectangleInsets(5.0, 5.0, 5.0, 5.0)); pp.setDomainCrosshairVisible(true); pp.setRangeCrosshairVisible(true); } } @Override public boolean _init(final IScope scope) throws GamaRuntimeException { history = new StringBuilder(500); lastValues.clear(); ; IExpression string1 = getFacet(IKeyword.TYPE); if (string1 != null) { String t = Cast.asString(scope, string1.value(scope)); type = IKeyword.SERIES.equals(t) ? SERIES_CHART : IKeyword.HISTOGRAM.equals(t) ? HISTOGRAM_CHART : IKeyword.PIE.equals(t) ? PIE_CHART : IKeyword.BOX_WHISKER.equals(t) ? BOX_WHISKER_CHART : IKeyword.SCATTER.equals(t) ? SCATTER_CHART : XY_CHART; } IExpression color = getFacet(IKeyword.AXES); if (color != null) { axesColor = Cast.asColor(scope, color.value(scope)); } IExpression color1 = getFacet(IKeyword.BACKGROUND); if (color1 != null) { backgroundColor = Cast.asColor(scope, color1.value(scope)); } IExpression string = getFacet(IKeyword.STYLE); if (string != null) { style = Cast.asString(scope, string.value(scope)); // TODO Verifier style; } IExpression face = getFacet(ChartLayerStatement.TICKFONTFACE); if (face != null) { tickFontFace = Cast.asString(scope, face.value(scope)); } face = getFacet(ChartLayerStatement.LABELFONTFACE); if (face != null) { labelFontFace = Cast.asString(scope, face.value(scope)); } face = getFacet(ChartLayerStatement.LEGENDFONTFACE); if (face != null) { legendFontFace = Cast.asString(scope, face.value(scope)); } face = getFacet(ChartLayerStatement.TITLEFONTFACE); if (face != null) { titleFontFace = Cast.asString(scope, face.value(scope)); } face = getFacet(ChartLayerStatement.TICKFONTSIZE); if (face != null) { tickFontSize = Cast.asInt(scope, face.value(scope)); } face = getFacet(ChartLayerStatement.LABELFONTSIZE); if (face != null) { labelFontSize = Cast.asInt(scope, face.value(scope)); } face = getFacet(ChartLayerStatement.LEGENDFONTSIZE); if (face != null) { legendFontSize = Cast.asInt(scope, face.value(scope)); } face = getFacet(ChartLayerStatement.TITLEFONTSIZE); if (face != null) { titleFontSize = Cast.asInt(scope, face.value(scope)); } face = getFacet(ChartLayerStatement.TICKFONTSTYLE); if (face != null) { tickFontStyle = toFontStyle(Cast.asString(scope, face.value(scope))); } face = getFacet(ChartLayerStatement.LABELFONTSTYLE); if (face != null) { labelFontStyle = toFontStyle(Cast.asString(scope, face.value(scope))); } face = getFacet(ChartLayerStatement.LEGENDFONTSTYLE); if (face != null) { legendFontStyle = toFontStyle(Cast.asString(scope, face.value(scope))); } face = getFacet(ChartLayerStatement.TITLEFONTSTYLE); if (face != null) { titleFontStyle = toFontStyle(Cast.asString(scope, face.value(scope))); } createChart(scope); createData(scope); // dataswithoutlists = datas; updateseries(scope); chart.setNotify(false); return true; } int toFontStyle(final String style) { if (style.equals("bold")) { return Font.BOLD; } if (style.equals("italic")) { return Font.ITALIC; } return Font.PLAIN; } public void updateseries(final IScope scope) throws GamaRuntimeException { // datas=dataswithoutlists; datasfromlists = new ArrayList<ChartData>(); for (int dl = 0; dl < datalists.size(); dl++) { ChartDataList datalist = datalists.get(dl); Object val = datalist.valuelistexp.resolveAgainst(scope).value(scope); if (!(val instanceof GamaList)) { // GuiUtils.debug("chart list with no list..."); return; } List<List> values = Cast.asList(scope, val); if (datalist.doreverse) { List tempvalues = Cast.asList(scope, val); values = new ArrayList<List>(); if (tempvalues.get(0) instanceof GamaList) { IList nval = Cast.asList(scope, tempvalues.get(0)); for (int j = 0; j < nval.size(); j++) { List nl = new ArrayList(); nl.add(nval.get(j)); values.add(nl); } } else { // GuiUtils.debug("Reverse series but not list of list..." + tempvalues); return; } if (tempvalues.size() > 1) { for (int i = 1; i < tempvalues.size(); i++) { if (tempvalues.get(i) instanceof GamaList) { IList nval = Cast.asList(scope, tempvalues.get(i)); for (int j = 0; j < nval.size(); j++) { // Cast.asList(scope, values.get(j)).add(nval.get(j)); values.get(j).add(nval.get(j)); } } else { // GuiUtils.debug("Reverse series but not list of list..." + tempvalues); return; } } } // GuiUtils.debug("New Values"+values); } List defaultnames = new ArrayList<String>(); List defaultcolors = new ArrayList<GamaColor>(); for (int i = 0; i < values.size() + 1; i++) { defaultnames.add("data" + i); // defaultcolors.add(GamaColor.array[i]); if (i < 10) { if (i == 0) { defaultcolors.add(Cast.asColor(scope, Color.CYAN)); } if (i == 1) { defaultcolors.add(Cast.asColor(scope, Color.RED)); } if (i == 2) { defaultcolors.add(Cast.asColor(scope, Color.YELLOW)); } if (i == 3) { defaultcolors.add(Cast.asColor(scope, Color.GREEN)); } if (i == 4) { defaultcolors.add(Cast.asColor(scope, Color.BLUE)); } if (i == 5) { defaultcolors.add(Cast.asColor(scope, Color.PINK)); } if (i == 6) { defaultcolors.add(Cast.asColor(scope, Color.MAGENTA)); } if (i == 7) { defaultcolors.add(Cast.asColor(scope, Color.ORANGE)); } if (i == 8) { defaultcolors.add(Cast.asColor(scope, Color.LIGHT_GRAY)); } if (i == 9) { defaultcolors.add(Cast.asColor(scope, Color.DARK_GRAY)); } } if (i >= 10) { if (i < GamaColor.colors.size()) { defaultcolors.add(GamaColor.int_colors.values()[i]); } else { defaultcolors.add(GamaColor.getInt(Random.opRnd(scope, 10000))); } } } if (datalist.colorlistexp != null) { Object valcol = datalist.colorlistexp.resolveAgainst(scope).value(scope); if (valcol instanceof GamaList) { for (int c = 0; c < ((GamaList) valcol).size(); c++) { // if ( type == SERIES_CHART) // defaultcolors.set(c+1, Cast.asColor(scope, ((GamaList)valcol).get(c))); // else defaultcolors.set(c, Cast.asColor(scope, ((GamaList) valcol).get(c))); } } } boolean dynamicseriesnames = false; List<String> seriesnames = new ArrayList(); if (datalist.legendlistexp != null) { Object valc = datalist.legendlistexp.resolveAgainst(scope).value(scope); if (valc instanceof GamaList) { dynamicseriesnames = true; seriesnames = (GamaList) valc; for (int i = 0; i < Math.min(values.size(), seriesnames.size()); i++) { defaultnames.set(i, seriesnames.get(i) + "(" + i + ")"); if (type == SERIES_CHART && ((XYPlot) chart.getPlot()).getDataset(i + 1) != null) { if (((DefaultTableXYDataset) ((XYPlot) chart.getPlot()).getDataset(i + 1)) .getSeriesCount() > 0) { ((DefaultTableXYDataset) ((XYPlot) chart.getPlot()).getDataset(i + 1)).getSeries(0) .setKey(seriesnames.get(i) + "(" + i + ")"); } } } if (values.size() > seriesnames.size()) { for (int i = seriesnames.size(); i < values.size(); i++) { defaultnames.set(i, "(" + i + ")"); } } } else { for (int i = values.size(); i < values.size(); i++) { defaultnames.set(i, "(" + i + ")"); } } } int nbseries = values.size(); // if ( type==SERIES_CHART ) nbseries++; // ChartData first=datas.get(0); if (type == HISTOGRAM_CHART) { ((DefaultCategoryDataset) dataset).clear(); } if (type == PIE_CHART) { ((DefaultPieDataset) dataset).clear(); } if (nbseries > datalist.previoussize) { for (int i = datalist.previoussize; i < nbseries; i++) { AbstractRenderer r; try { r = datalist.renderer.getClass().newInstance(); if (XYLineAndShapeRenderer.class.isAssignableFrom(r.getClass())) { ((XYLineAndShapeRenderer) r).setBaseShapesFilled( ((XYLineAndShapeRenderer) datalist.renderer).getBaseShapesFilled()); ((XYLineAndShapeRenderer) r).setBaseShapesVisible( ((XYLineAndShapeRenderer) datalist.renderer).getBaseShapesVisible()); ((XYLineAndShapeRenderer) r).setSeriesLinesVisible(0, ((XYLineAndShapeRenderer) datalist.renderer).getSeriesLinesVisible(0)); } ChartData newdata; newdata = ChartDataListStatement.newChartData(scope, r, Cast.asString(scope, defaultnames.get(i)), Cast.asColor(scope, defaultcolors.get(i)), values.get(i)); datas.add(newdata); datasfromlists.add(newdata); if (type == SERIES_CHART || type == XY_CHART || type == SCATTER_CHART) { final XYPlot plot = (XYPlot) chart.getPlot(); final String legend = newdata.getName(); // if (dataset==null) // dataset = new XYDataset(); if (type == SERIES_CHART || type == XY_CHART) { dataset = new DefaultTableXYDataset(); final XYSeries serie = new XYSeries(legend, false, false); ((DefaultTableXYDataset) dataset).addSeries(serie); } else { dataset = new XYSeriesCollection(); final XYSeries serie = new XYSeries(legend, false, true); ((XYSeriesCollection) dataset).addSeries(serie); } expressions_index.put(legend, datas.size() - 1); plot.setRenderer(datas.size() - 1, (XYItemRenderer) newdata.getRenderer(), false); final Color c = newdata.getColor(); plot.getRenderer(datas.size() - 1).setSeriesPaint(0, c); // if ((i>0)||(type==XY_CHART)) plot.setDataset(datas.size() - 1, (XYDataset) dataset); history.append(legend); history.append(','); } if (type == HISTOGRAM_CHART) { final CategoryPlot plot = (CategoryPlot) chart.getPlot(); int l = 0; for (final ChartData e : datas) { // String legend = e.getName(); // ((DefaultCategoryDataset) dataset).setValue(0d, new Integer(0), legend/* , legend // */); final String legend = e.getName(); if (!CategoryItemRenderer.class.isInstance(e.getRenderer())) { e.renderer = new BarRenderer(); } plot.setRenderer(l, (CategoryItemRenderer) e.getRenderer(), false); final Color c = e.getColor(); plot.getRenderer(l).setSeriesPaint(0, c); // plot.setDataset(i, (DefaultCategoryDataset) dataset); // } l++; history.append(legend); history.append(','); } if (history.length() > 0) { history.deleteCharAt(history.length() - 1); } history.append(Strings.LN); // plot.setDataset((DefaultCategoryDataset) dataset); } if (type == PIE_CHART) { int l = 0; // dataset = new DefaultPieDataset(); final PiePlot plot = (PiePlot) chart.getPlot(); for (final ChartData e : datas) { final String legend = e.getName(); ((DefaultPieDataset) dataset).insertValue(l++, legend, null); history.append(legend); history.append(','); } if (history.length() > 0) { history.deleteCharAt(history.length() - 1); } history.append(Strings.LN); history.append(Strings.LN); // plot.setDataset((DefaultPieDataset) dataset); l = 0; for (final ChartData e : datas) { plot.setSectionPaint(l++, e.getColor()); } plot.setLabelGenerator(new StandardPieSectionLabelGenerator("{0} = {1} ({2})")); if (exploded) { for (final Object c : ((DefaultPieDataset) dataset).getKeys()) { plot.setExplodePercent((Comparable) c, 0.20); } } } } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } } datalist.previoussize = nbseries; } boolean dynamiccategorynames = false; List<String> categorynames = new ArrayList<String>(); /* * if (datalist.categlistexp!=null) * { * Object valc=datalist.categlistexp.resolveAgainst(scope).value(scope); * if ((valc instanceof GamaList)) * { * dynamiccategorynames=true; * categorynames=(GamaList)valc; * } * * if (type==HISTOGRAM_CHART) * { * for ( int i=0; i<values.size(); i++ ) { * GamaList x = new GamaList(); * Object obj = values.get(i); * if ( obj instanceof GamaList ) { * x = (GamaList) obj; * // clearvalues=true; * if (dynamiccategorynames) * { * for (int j=0;j<x.length(scope);j++) * if (j<categorynames.size()) * { * ((DefaultCategoryDataset) dataset).setValue(Cast.asFloat(scope, * x.get(j)).doubleValue(),(String)defaultnames.get(i),categorynames.get(j).toString()+"("+j+")"); * } * else * ((DefaultCategoryDataset) dataset).setValue(Cast.asFloat(scope, * x.get(j)).doubleValue(),(String)defaultnames.get(i),"("+j+")"); * } * else * { * for (int j=0;j<x.length(scope);j++) * ((DefaultCategoryDataset) dataset).setValue(Cast.asFloat(scope, * x.get(j)).doubleValue(),(String)defaultnames.get(i), new Integer(j)); * * } * } else { * ((DefaultCategoryDataset) dataset).setValue(Cast.asFloat(scope, obj).doubleValue(), new * Integer(0),(String)defaultnames.get(i)); * } * } * } * } */ if (chart.getLegend() == null) { chart.addLegend(new LegendTitle(chart.getPlot())); } // LegendTitle legend = chart.getLegend(); // GuiUtils.debug("dyncateg:"+defaultnames); // GuiUtils.debug("legend:"+legend); for (int i = 0; i < nbseries; i++) { ChartData first = datas.get(i); if (type == SERIES_CHART) { first = datas.get(i + 1); } first.lastvalue = values.get(i); } } } public void clearvalues(final IScope scope) { // DefaultCategoryDataset=new DefaultCategoryDataset(); if (type == PIE_CHART) { ((DefaultPieDataset) dataset).clear(); } else { if (dataset != null) { ((DefaultCategoryDataset) dataset).clear(); } // if (chart.getLegend()!=null) chart.removeLegend(); for (int dl = 0; dl < datalists.size(); dl++) { ChartDataList datalist = datalists.get(dl); // datalist.previoussize=0; // GamaList defaultnames =new GamaList<String>(Types.STRING); boolean dynamicseriesnames = false; if (datalist.legendlistexp != null) { Object valc = datalist.legendlistexp.resolveAgainst(scope).value(scope); if (valc instanceof GamaList) { dynamicseriesnames = true; GamaList seriesnames = (GamaList) valc; for (int i = 0; i < Math.min(datas.size(), seriesnames.size()); i++) { datas.get(i).setName(seriesnames.get(i) + "(" + i + ")"); } } } boolean dynamiccategorynames = false; List<String> categorynames = new ArrayList<String>(); if (datalist.categlistexp != null) { Object valc = datalist.categlistexp.resolveAgainst(scope).value(scope); if (valc instanceof GamaList) { dynamiccategorynames = true; categorynames = (GamaList) valc; } } // if (dynamiccategorynames) // { // ((DefaultCategoryDataset) dataset).clear(); // } if (dataset != null) { for (final ChartData d : datas) { List x = new ArrayList(); Object obj = d.getValue(scope); if (obj instanceof GamaList) { x = (GamaList) obj; // clearvalues=true; if (dynamiccategorynames) { for (int j = 0; j < x.size(); j++) { if (j < categorynames.size()) { ((DefaultCategoryDataset) dataset).setValue( Cast.asFloat(scope, x.get(j)).doubleValue(), d.getName(), categorynames.get(j).toString() + "(" + j + ")"); } else { ((DefaultCategoryDataset) dataset).setValue( Cast.asFloat(scope, x.get(j)).doubleValue(), d.getName(), "(" + j + ")"); } } } else { for (int j = 0; j < x.size(); j++) { ((DefaultCategoryDataset) dataset).setValue( Cast.asFloat(scope, x.get(j)).doubleValue(), d.getName(), Integer.valueOf(j)); } } } else { ((DefaultCategoryDataset) dataset).setValue(Cast.asFloat(scope, obj).doubleValue(), new Integer(0), d.getName()); } switch (type) { case PIE_CHART: { // ((DefaultPieDataset) dataset).setValue(s, n); break; } case HISTOGRAM_CHART: { // GuiUtils.debug("ChartLayerStatement._step row " + ((DefaultCategoryDataset) // dataset).getRowCount() + // " col " + ((DefaultCategoryDataset) dataset).getColumnCount()); // ((DefaultCategoryDataset) dataset).setValue(n, new Integer(0), s); break; } } history.append(0); history.append(','); } } } } if (chart.getLegend() == null) { // LegendTitle nouvleg=new LegendTitle(chart.getPlot()); // chart.addLegend(nouvleg); // nouvleg. } } @Override public boolean _step(final IScope scope) throws GamaRuntimeException { lastComputeCycle = (long) scope.getClock().getCycle(); if (datalists.size() > 0) { updateseries(scope); } if (type == XY_CHART || type == SERIES_CHART || type == SCATTER_CHART) { computeSeries(scope, lastComputeCycle); return true; } /* * switch (type) { * case XY_CHART: * computeSeries(scope, lastComputeCycle); * case SERIES_CHART: * computeSeries(scope, lastComputeCycle); * return true; * } */ boolean clearvalues = false; int cpt = 0; for (final ChartData d : datas) { List x = new ArrayList(); Object obj = d.getValue(scope); if (obj instanceof GamaList) { x = (GamaList) obj; clearvalues = true; if (type != XY_CHART) { for (int j = 0; j < x.size(); j++) { lastValues.put(d.getName(), Cast.asFloat(scope, x.get(j))); } } } else { x.add(obj); // if ( type != XY_CHART || type != SCATTER_CHART ) { lastValues.put(d.getName(), Cast.asFloat(scope, x.get(x.size() - 1))); // } } } if (clearvalues) { clearvalues(scope); } else { for (final Map.Entry<String, Double> d : lastValues.entrySet()) { String s = d.getKey(); final double n = d.getValue(); if (!(d instanceof GamaList)) { switch (type) { case PIE_CHART: { ((DefaultPieDataset) dataset).setValue(s, n); break; } case HISTOGRAM_CHART: { // ((DefaultCategoryDataset) dataset).setValue(Cast.asFloat(scope, // x.get(j)).doubleValue(),d.getName(),"("+j+")"); // GuiUtils.debug("ChartLayerStatement._step row " + ((DefaultCategoryDataset) // dataset).getRowCount() + // " col " + ((DefaultCategoryDataset) dataset).getColumnCount()); ((DefaultCategoryDataset) dataset).setValue(n, Integer.valueOf(0), s/* , s */); ((CategoryPlot) chart.getPlot()).getRenderer().setSeriesPaint(0, datas.get(cpt).color); cpt++; break; } } } history.append(n); history.append(','); } } history.deleteCharAt(history.length() - 1); history.append(Strings.LN); return true; } /** * @throws GamaRuntimeException * @param scope * @param cycle */ private void computeSeries(final IScope scope, final long cycle) throws GamaRuntimeException { if (datas.isEmpty()) { return; } List x = new ArrayList(); Object obj = datas.get(0).getValue(scope); if (type == SERIES_CHART && scope.getAgentScope() instanceof BatchAgent) { // if (BatchAgent.class.isAssignableFrom(scope.getClass())) obj = ((BatchAgent) scope.getAgentScope()).getRunNumber(); } boolean cumulative = false; if (obj instanceof GamaList) { x = (GamaList) obj; } else { x.add(obj); } for (int i = 0; i < x.size(); i++) { history.append(x.get(i)); history.append(','); } if (!(type == SERIES_CHART & datas.size() < 2)) { for (int i = 0; i < datas.size(); i++) { if (!datasfromlists.contains(datas.get(i))) { if (type == SERIES_CHART & i == 0) { i++; } XYPlot plot = (XYPlot) chart.getPlot(); // DefaultTableXYDataset data = (DefaultTableXYDataset) plot.getDataset(i); XYDataset data = plot.getDataset(i); XYSeries serie = new XYSeries(0, false, false); if (type == SERIES_CHART || type == XY_CHART) { serie = ((DefaultTableXYDataset) data).getSeries(0); } if (type == SCATTER_CHART) { serie = ((XYSeriesCollection) data).getSeries(0); } List n = new ArrayList(); Object o = datas.get(i).getValue(scope); if (o instanceof GamaList) { n = (GamaList) o; } else { cumulative = true; n.add(o); } if (!cumulative) { if (type == SERIES_CHART || type == XY_CHART) { final XYSeries nserie = new XYSeries(serie.getKey(), false, false); ((DefaultTableXYDataset) data).removeSeries(0); ((DefaultTableXYDataset) data).addSeries(nserie); serie = nserie; // serie.clear(); } if (type == SCATTER_CHART) { final XYSeries nserie = new XYSeries(serie.getKey(), false, true); ((XYSeriesCollection) data).removeSeries(0); ((XYSeriesCollection) data).addSeries(nserie); serie = nserie; // serie.clear(); } } // java.lang.System.out.println("gr"+n); for (int j = 0; j < n.size(); j++) { if (type == SERIES_CHART) { double d2 = Cast.asFloat(scope, n.get(j)); double d1; if (cumulative) { d1 = Cast.asFloat(scope, x.get(j)); } else { d1 = Cast.asFloat(scope, j); } serie.addOrUpdate(d1, d2); } else if (type == XY_CHART || type == SCATTER_CHART) { try { IList<Double> list = GamaListType.staticCast(scope, n.get(j), Types.FLOAT, false); double d1 = list.get(0); double d2 = list.get(1); if (cumulative) { serie.addOrUpdate(d1, d2); } else { serie.addOrUpdate(d1, d2); } } catch (IndexOutOfBoundsException e) { // GamaRuntimeException g = GamaRuntimeException.create(e,scope); // g.addContext("each point value should be a gama-point or a 2-float list, value here: "+(Cast.asList(scope, n.get(j)))); GamaRuntimeException g = GamaRuntimeException.error( "each point value should be a gama-point or a 2-float list, value here: " + Cast.asList(scope, n.get(j)), scope); GAMA.reportAndThrowIfNeeded(scope, g, true); // TODO Auto-generated catch block } } history.append(n.get(j)); history.append(','); } } } } history.deleteCharAt(history.length() - 1); history.append(Strings.LN); } @Override public short getType() { return ILayerStatement.CHART; } @Override public void dispose() { chart = null; super.dispose(); } public void saveHistory() { IScope scope = output.getScope().copy(); if (scope == null) { return; } try { Files.newFolder(scope, chartFolder); String file = chartFolder + "/" + "chart_" + getName() + ".csv"; BufferedWriter bw; file = FileUtils.constructAbsoluteFilePath(scope, file, false); bw = new BufferedWriter(new FileWriter(file)); bw.append(history); bw.close(); } catch (final Exception e) { e.printStackTrace(); return; } finally { GAMA.releaseScope(scope); } } }