JFreeChart: StackedXYBarChart
Так получается что по работе я все дальше отхожу от JavaScript в сторону Java. Я в принципе я рад, но дело в том, что писать становиться все сложнее, потому что показывать какие-то мелкие части большой системы не имеет никакого смысла, их зачастую нельзя даже использовать повторно. Но вот недавно мне пришлось использовать библиотеку под названием JFreeChart и о ней я сейчас расскажу.
Библиотека очень приятная на ощупь! Прекрасная демка, скачиваешь загрузчик, который сам тянет все примеры, смотришь, все хорошо, до того момента пока не переходишь на закладку "исходный код", а там с тебя просят за исходники заплатить. Простите, но это жлобство, мало того, что сама библиотека платная, так еще и исходники примеров платные. Первая мысль - декомпилировать, а вторая поискать в Google, который с радостью отдает все исходники по названию примера?. Пошаманив немного я изобразил то, что требовалось. Но об этом чуть позже.
В принципе примеры сделаны очень грамотно, но как всегда есть одно но, то что требуется сделать проблематично просмотреть, поскольку создается новый график без каких либо методов вывода себя. Вот в первом примере я и покажу как легко отображать график для его отладки при создании, для этого отнаследуюсь от фрейма и буду выдавать всплывающее окошко с картинкой. Кстати у этого окошка есть настройки и тултипы, которых лишены все остальные примеры.
package ua.mabp.kiev;
/**
* Created by IntelliJ IDEA.
* User: mabp
* Date: 27.05.2008
* Time: 23:18:42
*/
import java.awt.*;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import javax.swing.*;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.labels.StandardXYItemLabelGenerator;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StackedXYBarRenderer;
import org.jfree.data.time.TimeTableXYDataset;
import org.jfree.data.time.Year;
import org.jfree.data.xy.TableXYDataset;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.RectangleInsets;
import org.jfree.ui.RefineryUtilities;
import org.jfree.ui.VerticalAlignment;
public class StackedXYBarChartFrame extends ApplicationFrame {
/**
* Создаёт новый фрейм с графиком
* @param title Заголовок окна
*/
public StackedXYBarChartFrame(String title) {
super(title);
// Создаём новый график
JFreeChart chart = createChart(createDataset());
// На панеле
ChartPanel chartPanel = new ChartPanel(chart);
// С размерами 450*450
chartPanel.setPreferredSize(new Dimension(450, 450));
// И ползунками если необходимо
JScrollPane sp = new JScrollPane(chartPanel);
sp.setPreferredSize(new Dimension(500, 500));
sp.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
setContentPane(sp);
}
/**
* Наполняет Set данными для построения графика
* @return Данные для построения
*/
private static TableXYDataset createDataset() {
// Типа данные
TimeTableXYDataset dataset = new TimeTableXYDataset();
dataset.add(new Year(2002), 1000, "Blue");
dataset.add(new Year(2003), 1100, "Blue");
dataset.add(new Year(2002), 0, "Red");
dataset.add(new Year(2003), 50, "Red");
return dataset;
}
/**
* Создаёт новый график по данным
* @param dataset данные для построения
* @return график
*/
private static JFreeChart createChart(TableXYDataset dataset) {
// OX - ось абсцисс
// задаем название оси
DateAxis domainAxis = new DateAxis("Year");
// Показываем стрелочку вправо
domainAxis.setPositiveArrowVisible(true);
// Задаем отступ от графика
domainAxis.setUpperMargin(0.2);
// OY - ось ординат
// Задаём название оси
NumberAxis rangeAxis = new NumberAxis("Color");
// Задаём величину деления
rangeAxis.setStandardTickUnits(NumberAxis.createStandardTickUnits());
rangeAxis.setTickUnit(new NumberTickUnit(200));
// Показываем стрелочку вверх
rangeAxis.setPositiveArrowVisible(true);
// Render
// Создаем стопковый (не знаю как лучше перевести) график
// 0.02 - расстояние между столбиками
StackedXYBarRenderer renderer = new StackedXYBarRenderer(0.02);
// без рамки
renderer.setDrawBarOutline(false);
// цвета для каждого элемента стопки
renderer.setSeriesPaint(0, Color.blue);
renderer.setSeriesPaint(1, Color.red);
// Задаём формат и текст подсказки
renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator("{0} : {1} = {2} tonnes", new SimpleDateFormat("yyyy"), new DecimalFormat("#,##0")));
renderer.setSeriesItemLabelGenerator(0, new StandardXYItemLabelGenerator());
renderer.setSeriesItemLabelGenerator(1, new StandardXYItemLabelGenerator());
// Делаем её видимой
renderer.setSeriesItemLabelsVisible(0, true);
renderer.setSeriesItemLabelsVisible(1, true);
// И описываем её шрифт
renderer.setSeriesItemLabelFont(0, new Font("Serif", Font.BOLD, 10));
renderer.setSeriesItemLabelFont(1, new Font("Serif", Font.BOLD, 10));
// Plot
// Создаем область рисования
XYPlot plot = new XYPlot(dataset, domainAxis, rangeAxis, renderer);
// Закрашиваем
plot.setBackgroundPaint(Color.white);
// Закрашиваем сетку
plot.setDomainGridlinePaint(Color.white);
plot.setRangeGridlinePaint(Color.white);
// Отступ от осей
plot.setAxisOffset(new RectangleInsets(0D, 0D, 10D, 10D));
plot.setOutlinePaint(null);
// Chart
// Создаем новый график
JFreeChart chart = new JFreeChart(plot);
// Закрашиваем
chart.setBackgroundPaint(Color.white);
// Перемещаем легенду в верхний правый угол
chart.getLegend().setPosition(RectangleEdge.RIGHT);
chart.getLegend().setVerticalAlignment(VerticalAlignment.TOP);
return chart;
}
public static void main(String[] args) {
// Создаем новый фрейм
StackedXYBarChartFrame demo = new StackedXYBarChartFrame("JFreeChart: StackedXYBarChart");
demo.pack();
// И показываем
RefineryUtilities.centerFrameOnScreen(demo);
demo.setVisible(true);
}
}
Я надеюсь, я достаточно продокументировал код, что бы было понятно, что я делаю. В результате его выполнения получаем всплывающее окошко с графиком, все хорошо, быстро и удобно, но нам все-таки нужен график и, скорее всего мы будем его либо показывать в браузер, либо сохранять в файл, значит, нам нужно сделать так, чтобы его можно было передать в OutputStream. Для выброса в поток и записи в файл предусмотрены две утилиты, каждая из которых умеет работать с двумя форматами PNG и JPEG, GIF - платный формат :(. Так вот тут есть косяк при сохранении в JPEG почему-то график окрашивается в розовый цвет. лечение от этого бага я не нашел, да особо и не искал так как PNG меня вполне устраивал. Итак, выбрасываем в поток:
package ua.mabp.kiev;
/**
* Created by IntelliJ IDEA.
* User: mabp
* Date: 27.05.2008
* Time: 23:18:42
*/
import java.awt.*;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.labels.StandardXYItemLabelGenerator;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StackedXYBarRenderer;
import org.jfree.data.time.TimeTableXYDataset;
import org.jfree.data.time.Year;
import org.jfree.data.xy.TableXYDataset;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.RectangleInsets;
import org.jfree.ui.VerticalAlignment;
public class StackedXYBarChartPNG extends JFreeChart {
/**
* Создаёт новый график по данным
* @param dataset данные для построения
*/
public StackedXYBarChartPNG(TableXYDataset dataset) {
super(new XYPlot());
// OX
DateAxis domainAxis = new DateAxis("Month");
domainAxis.setPositiveArrowVisible(true);
domainAxis.setUpperMargin(0.2);
// OY
NumberAxis rangeAxis = new NumberAxis("Tonnes");
rangeAxis.setStandardTickUnits(NumberAxis.createStandardTickUnits());
rangeAxis.setTickUnit(new NumberTickUnit(200));
rangeAxis.setPositiveArrowVisible(true);
// Render
StackedXYBarRenderer renderer = new StackedXYBarRenderer(0.02);
renderer.setDrawBarOutline(false);
renderer.setSeriesPaint(0, Color.blue);
renderer.setSeriesPaint(1, Color.red);
renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator("{0} : {1} = {2} tonnes", new SimpleDateFormat("yyyy"), new DecimalFormat("#,##0")));
renderer.setSeriesItemLabelGenerator(0, new StandardXYItemLabelGenerator());
renderer.setSeriesItemLabelGenerator(1, new StandardXYItemLabelGenerator());
renderer.setSeriesItemLabelsVisible(0, true);
renderer.setSeriesItemLabelsVisible(1, true);
renderer.setSeriesItemLabelFont(0, new Font("Serif", Font.BOLD, 10));
renderer.setSeriesItemLabelFont(1, new Font("Serif", Font.BOLD, 10));
// Plot
XYPlot plot = getXYPlot();
plot.setDataset(dataset);
plot.setDomainAxis(domainAxis);
plot.setRangeAxis(rangeAxis);
plot.setRenderer(renderer);
plot.setBackgroundPaint(Color.white);
plot.setDomainGridlinePaint(Color.white);
plot.setRangeGridlinePaint(Color.white);
plot.setAxisOffset(new RectangleInsets(0D, 0D, 10D, 10D));
plot.setOutlinePaint(null);
// Chart
setBackgroundPaint(Color.white);
getLegend().setPosition(RectangleEdge.RIGHT);
getLegend().setVerticalAlignment(VerticalAlignment.TOP);
}
/**
* Наполняет Set данными для построения графика
* @return Данные для построения
*/
private static TableXYDataset createDataset() {
TimeTableXYDataset dataset = new TimeTableXYDataset();
dataset.add(new Year(2002), 1000, "Blue");
dataset.add(new Year(2003), 1100, "Blue");
dataset.add(new Year(2002), 0, "Red");
dataset.add(new Year(2003), 50, "Red");
return dataset;
}
/**
* Создает и сохраняет график
* @param args ничего не передается
*/
public static void main(String[] args) {
// строим график
JFreeChart chart = new StackedXYBarChartPNG(createDataset());
chart.setBorderVisible(false);
chart.setAntiAlias(true);
// выбрасывем в поток
try {
// например в файл
OutputStream stream = new FileOutputStream("chart.png");
ChartUtilities.writeChartAsPNG(stream, chart, 500, 500);
} catch(IOException e) {
System.err.println("Failed to render chart as png: "+ e.getMessage());
e.printStackTrace();
}
}
}
Две основные функции не поменялись, единственно что из конструктора можно выкинуть все что касается тултипов так как они естественно не работают.
Поменялся PSVM он теперь приспособлен для обработки графика внешними методами. Файлик chart.png можно будет найти в корне проекта и посмотреть, что получилось. Но было бы некрасиво столько написать и не дать посмотреть на результат, возможно, кто-то не может собрать проект, а кто-то просто не хочет, поэтому я завернул все это хозяйство в апплет и сделал из этого третий пример. Он получился самым легким из-за отсутствия всех дополнительных методов, есть только один метод для рисования имплементирующий функционал апплета.
package ua.mabp.kiev;
/**
* Created by IntelliJ IDEA.
* User: mabp
* Date: 27.05.2008
* Time: 23:18:42
*/
import java.applet.Applet;
import java.awt.*;
import java.text.SimpleDateFormat;
import java.text.DecimalFormat;
import org.jfree.data.xy.TableXYDataset;
import org.jfree.data.time.TimeTableXYDataset;
import org.jfree.data.time.Year;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.renderer.xy.StackedXYBarRenderer;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.labels.StandardXYItemLabelGenerator;
import org.jfree.chart.JFreeChart;
import org.jfree.ui.RectangleInsets;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.VerticalAlignment;
public class StackedXYBarChartApplet extends Applet {
/**
* Наполняет Set данными для построения графика
* @return Данные для построения
*/
private static TableXYDataset createDataset() {
TimeTableXYDataset dataset = new TimeTableXYDataset();
dataset.add(new Year(2002), 1000, "Blue");
dataset.add(new Year(2003), 1100, "Blue");
dataset.add(new Year(2002), 0, "Red");
dataset.add(new Year(2003), 50, "Red");
return dataset;
}
/**
* Создаёт новый график по данным
* @param dataset данные для построения
* @return график
*/
private static JFreeChart createChart(TableXYDataset dataset) {
// OX
DateAxis domainAxis = new DateAxis("Month");
domainAxis.setPositiveArrowVisible(true);
domainAxis.setUpperMargin(0.2);
// OY
NumberAxis rangeAxis = new NumberAxis("Tonnes");
rangeAxis.setStandardTickUnits(NumberAxis.createStandardTickUnits());
rangeAxis.setTickUnit(new NumberTickUnit(200));
rangeAxis.setPositiveArrowVisible(true);
// Render
StackedXYBarRenderer renderer = new StackedXYBarRenderer(0.02);
renderer.setDrawBarOutline(false);
renderer.setSeriesPaint(0, Color.blue);
renderer.setSeriesPaint(1, Color.red);
renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator("{0} : {1} = {2} tonnes", new SimpleDateFormat("yyyy"), new DecimalFormat("#,##0")));
renderer.setSeriesItemLabelGenerator(0, new StandardXYItemLabelGenerator());
renderer.setSeriesItemLabelGenerator(1, new StandardXYItemLabelGenerator());
renderer.setSeriesItemLabelsVisible(0, true);
renderer.setSeriesItemLabelsVisible(1, true);
renderer.setSeriesItemLabelFont(0, new Font("Serif", Font.BOLD, 10));
renderer.setSeriesItemLabelFont(1, new Font("Serif", Font.BOLD, 10));
// Plot
XYPlot plot = new XYPlot(dataset, domainAxis, rangeAxis, renderer);
plot.setBackgroundPaint(Color.white);
plot.setDomainGridlinePaint(Color.white);
plot.setRangeGridlinePaint(Color.white);
plot.setAxisOffset(new RectangleInsets(0D, 0D, 10D, 10D));
plot.setOutlinePaint(null);
// Chart
JFreeChart chart = new JFreeChart(plot);
chart.setBackgroundPaint(Color.white);
chart.getLegend().setPosition(RectangleEdge.RIGHT);
chart.getLegend().setVerticalAlignment(VerticalAlignment.TOP);
return chart;
}
/**
* Вот тут вся фишка апплета
* @param g Графика
*/
public void paint(Graphics g){
JFreeChart chart = createChart(createDataset());
chart.draw((Graphics2D)g, getBounds());
}
}
Итак смотрим что получилось в результате:
Свежие комментарии