how to change pie chart colors of JFreeChart? - jfreechart

how to customize the colors of JFreeChart graphic.
lets see my java code :
private StreamedContent chartImage ;
public void init(){
JFreeChart jfreechart = ChartFactory.createPieChart("title", createDataset(), true, true, false);
File chartFile = new File("dynamichart");
ChartUtilities.saveChartAsPNG(chartFile, jfreechart, 375, 300);
chartImage = new DefaultStreamedContent(new FileInputStream( chartFile), "image/png");
}
public PieDataset createDataset() {
DefaultPieDataset dataset = new DefaultPieDataset();
dataset.setValue("J-2", 10);
dataset.setValue("J-1", 15);
dataset.setValue("J", 50);
dataset.setValue("J+1", 20);
dataset.setValue("J+2", 15);
return dataset;
}
html page :
<p:graphicImage id="MyImage" value="#{beanCreateImage.chartImage}" />

You can change the color of single pieces like this:
JFreeChart chart = ChartFactory.createPieChart("title", createDataset(), true, true, false);
PiePlot plot = (PiePlot) chart.getPlot();
plot.setSectionPaint("J+1", Color.black);
plot.setSectionPaint("J-1", new Color(120, 0, 120));
// or do this, if you are using an older version of JFreeChart:
//plot.setSectionPaint(1, Color.black);
//plot.setSectionPaint(3, new Color(120, 0, 120));
So with your code, all the pies are colored automatically, after my code changes, the J-1 and J+1 have a fixed color, the rest gets automatically colored.

To set the colous for a chart you can implement the DrawingSupplier inferface in this case I've used DefaultDrawingSupplier:
public class ChartDrawingSupplier extends DefaultDrawingSupplier {
public Paint[] paintSequence;
public int paintIndex;
public int fillPaintIndex;
{
paintSequence = new Paint[] {
new Color(227, 26, 28),
new Color(000,102, 204),
new Color(102,051,153),
new Color(102,51,0),
new Color(156,136,48),
new Color(153,204,102),
new Color(153,51,51),
new Color(102,51,0),
new Color(204,153,51),
new Color(0,51,0),
};
}
#Override
public Paint getNextPaint() {
Paint result
= paintSequence[paintIndex % paintSequence.length];
paintIndex++;
return result;
}
#Override
public Paint getNextFillPaint() {
Paint result
= paintSequence[fillPaintIndex % paintSequence.length];
fillPaintIndex++;
return result;
}
}
Then include this code in your `init()' method
JFreeChart jfreechart = ChartFactory.createPieChart("title", createDataset(), true, true, false);
Plot plot = jfreechart.getPlot();
plot.setDrawingSupplier(new ChartDrawingSupplier());
...

You can customize the colors according to the labels while getting the data from the dataset:
// Add custom colors
PiePlot plot = (PiePlot) chart.getPlot();
for (int i = 0; i < dataset.getItemCount(); i++) {
if(dataset.getKey(i).equals("J+1")){
plot.setSectionPaint(i, Color.black);
}
}
You can also use a switch-case statement or the one you prefer.

Related

JFreeChart disable vertical gray areas of XYPlot

The code below plots a graph with unwanted vertical gray areas (stripes) corresponding with alternate domain ticks.
I have tried unsuccessfully to remove them from the graph to obtain a plot with white background.
I have been searching through the methods of XYPlot or NumberAxis (last try was setting to null xyplot.setDomainTickBandPaint(null); and xyplot.setRangeTickBandPaint(null);), but I have not experience enough with JFreeChart to know what method to use.
This is the code for the above graph:
public class MyPlotChart {
private static Color MetalColor = new Color(255, 152, 0);
static double[] yData = new double[] { 49.68, 49.18, 49.78, 49.65, 48.94, 50.02, 50.27};
static String[] labels = new String[] { "2021-10-28", "2021-10-29", "2021-11-01", "2021-11-02", "2021-11-03", "2021-11-04", "2021-11-05"};
public static void plot(String metal, int samples) throws IOException {
XYSeries series = new XYSeries(metal);
int i = 0;
for (i = 0; i < yData.length; i++) {
series.add(i, yData[i]);
}
XYDataset dataset = new XYSeriesCollection(series);
NumberAxis domain = new SymbolAxis(null, labels);
NumberAxis verticalAxis = new NumberAxis(null);
verticalAxis.setAutoRangeIncludesZero(false);
domain.setTickUnit(new NumberTickUnit(1.0));
domain.setMarkerBand(null);
double vericalTickUnit = (series.getMaxY() - series.getMinY()) / 5;
NumberFormat numberFormat = NumberFormat.getInstance(Locale.getDefault());
numberFormat.setRoundingMode(RoundingMode.HALF_DOWN);
numberFormat.setMinimumFractionDigits(2);
numberFormat.setMaximumFractionDigits(2);
NumberTickUnit nt = new NumberTickUnit(vericalTickUnit, numberFormat);
verticalAxis.setTickUnit(nt);
verticalAxis.setAutoRange(true);
verticalAxis.setRange(new Range(series.getMinY()-0.1, series.getMaxY()+0.1));
verticalAxis.setTickMarksVisible(true);
verticalAxis.setTickMarkInsideLength(3f);
XYSplineRenderer r = new XYSplineRenderer(10);
r.setSeriesPaint(0, MetalColor);
r.setDefaultShapesVisible(false);
r.setSeriesStroke(0, new BasicStroke(3.0f));
XYPlot xyplot = new XYPlot(dataset, domain, verticalAxis, r);
xyplot.getDomainAxis().setVerticalTickLabels(true);
xyplot.setDomainGridlinesVisible(false);
xyplot.setBackgroundImage(null);
xyplot.setBackgroundPaint(Color.WHITE);
Font font = xyplot.getDomainAxis().getTickLabelFont();
Font fontnew = new Font(font.getName(), Font.BOLD, 14);
xyplot.getDomainAxis().setTickLabelFont(fontnew);
xyplot.getRangeAxis().setTickLabelFont(fontnew);
JFreeChart chart = new JFreeChart(xyplot);
chart.removeLegend();//Remove legend
chart.setBackgroundPaint(Color.WHITE);
String fileName = "myChart"+metal+samples+"TEST.png";
ChartUtils.saveChartAsPNG(new File(fileName), chart, 600, 600);
}
public static void main(String[] args) throws IOException {
MyPlotChart.plot("metal", 7);
}
}
As suggested in the comment, I opted to use DateAxis which do not implement alternating background and also gives more accurate treatment for tick labels when the data is time related.
I have attached the code and the plot obtained:
public class MyPlotChart {
private static Color MetalColor = new Color(255, 152, 0);
static double[] yData = new double[] { 49.68, 49.18, 49.78, 49.65, 48.94, 50.02, 50.27 };
static String[] labels = new String[] { "2021-10-28", "2021-10-29", "2021-11-01", "2021-11-02", "2021-11-03",
"2021-11-04", "2021-11-05" };
public static void plot(String metal, int samples) throws IOException, ParseException {
SimpleDateFormat dateformatyyyy_MM_dd = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat dateformatdd_MM_yyyy = new SimpleDateFormat("dd-MM-yyyy");
XYSeries series = new XYSeries(metal);
for (int i = 0; i < yData.length; i++) {
Date date = dateformatyyyy_MM_dd.parse(labels[i]);
series.add(date.getTime(), yData[i]);
}
//Configure Vertical Axis
NumberAxis verticalAxis = new NumberAxis(null);
NumberFormat numberFormat = NumberFormat.getInstance(Locale.getDefault());
numberFormat.setRoundingMode(RoundingMode.HALF_DOWN);
numberFormat.setMinimumFractionDigits(2);
numberFormat.setMaximumFractionDigits(2);
double vericalTickUnit = (series.getMaxY() - series.getMinY()) / 7;
NumberTickUnit nt = new NumberTickUnit(vericalTickUnit, numberFormat);
verticalAxis.setTickUnit(nt);
double percentOverRange = 0.05;// 2%
double initalRange = series.getMaxY() - series.getMinY();
double increase = initalRange * percentOverRange;
verticalAxis.setRange(new Range(series.getMinY()-increase, series.getMaxY()+increase));
verticalAxis.setAutoRange(true);
verticalAxis.setAutoRangeIncludesZero(false);
verticalAxis.setTickMarksVisible(true);
verticalAxis.setTickMarkInsideLength(3f);
//Configure Domain Axis
DateAxis domainAxis = new DateAxis(null);
domainAxis.setTickUnit(new DateTickUnit(DateTickUnitType.DAY, 1, dateformatdd_MM_yyyy));
//Configure Renderer
XYSplineRenderer r = new XYSplineRenderer(10);
r.setSeriesPaint(0, MetalColor);
r.setDefaultShapesVisible(false);
r.setSeriesStroke(0, new BasicStroke(3.0f));
XYDataset dataset = new XYSeriesCollection(series);
XYPlot xyplot = new XYPlot(dataset, domainAxis, verticalAxis, r);
xyplot.getDomainAxis().setVerticalTickLabels(true);
xyplot.setDomainGridlinesVisible(false);
xyplot.setBackgroundImage(null);
xyplot.setBackgroundPaint(Color.WHITE);
Font font = xyplot.getDomainAxis().getTickLabelFont();
Font fontnew = new Font(font.getName(), Font.BOLD, 14);
xyplot.getDomainAxis().setTickLabelFont(fontnew);
xyplot.getRangeAxis().setTickLabelFont(fontnew);
JFreeChart chart = new JFreeChart(xyplot);
chart.removeLegend();// Remove legend
chart.setBackgroundPaint(Color.WHITE);
String fileName = "myChart" + metal + samples + "TEST.png";
ChartUtils.saveChartAsPNG(new File(fileName), chart, 600, 600);
}
public static void main(String[] args) throws IOException, ParseException {
MyPlotChart.plot("metal", 7);
}
}

How to optimally find the smallest image rendered by a WPF control

I am using the FormulaControl from WPF-Math to render a bitmap for a tek equation. The bitmap will be delivered as content over a web service ( slack ). There is no desktop component. I am only using the WPF framework to try to capture the image from the tek control. The code for the renderer component is
public static class Renderer
{
private static readonly StaTaskScheduler _StaTaskScheduler = new StaTaskScheduler( 1 );
public static async Task<string> GenerateImage(string formula)
{
string Build()
{
var control = new FormulaControl
{
Formula = formula
, Background = Brushes.White
};
control.Measure(new Size(300, 300));
control.Arrange(new Rect(new Size(300, 300)));
var bmp = new RenderTargetBitmap(300, 300, 96, 96, PixelFormats.Pbgra32);
bmp.Render(control);
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bmp));
var file = #"test.png";
using (Stream stm = File.Create(file))
encoder.Save(stm);
return file;
}
return await Task.Factory.StartNew
( Build, CancellationToken.None, TaskCreationOptions.None, _StaTaskScheduler );
}
}
Using the above code and the input
k_{n+1} = n^2 + k_n^2 - k_{n-1}
the below image is generated
As you can see, in this case, an arbitrary size of 300x300 is too big and for a different tek input it maybe too small.
The challenge is to generate a bitmap of exactly the correct size for the rendered equation. How can this be done?
One solution is to render to a large bitmap then auto crop the whitespace. There is a solution for auto cropping whitespace at
Cropping whitespace from image in C#
Using the ImageCrop class from above I modified the rendering code to
public static class Renderer
{
private static readonly StaTaskScheduler _StaTaskScheduler = new StaTaskScheduler( 1 );
public static Bitmap Convert( RenderTargetBitmap inmap )
{
MemoryStream stream = new MemoryStream();
BitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(inmap));
encoder.Save(stream);
Bitmap bitmap = new Bitmap(stream);
return bitmap;
}
public static async Task<string> GenerateImage(string formula)
{
string Build()
{
var control = new FormulaControl
{
Formula = formula
, Background = Brushes.White
};
control.Measure(new Size(300, 300));
control.Arrange(new Rect(new Size(300, 300)));
var bmp = new RenderTargetBitmap(300, 300, 96, 96, PixelFormats.Pbgra32);
bmp.Render(control);
var image = ImageCrop.AutoCrop( Convert( bmp ) );
var file = #"test.png";
image.Save( file,ImageFormat.Png );
return file;
}
return await Task.Factory.StartNew
( Build, CancellationToken.None, TaskCreationOptions.None, _StaTaskScheduler );
}
}
The output in my slackbot is now perfectly cropped.
Obviously this is not perfect as there is an upper bound on the render size.

Disable right click menu of JFreeChart

I want to disable the right click menu of JFreeChart.
I tried chartPanel.setPopupMenu(null), but it didn't work.
The following example creates a simple XYPlot with "panel.setPopupMenu( null )" disabling the popup Menu.
`
public class DisableChartPopupMenu extends ApplicationFrame {
public DisableChartPopupMenu(String title) {
super(title);
}
public static void main(final String[] args) {
(new DisableChartPopupMenu("example")).createChartNoPopupMenu();
}
public void createChartNoPopupMenu(){
final XYSeries series1 = new XYSeries("Series 1");
series1.add(10.0, 12353.3);
series1.add(20.0, 13734.4);
series1.add(30.0, 14525.3);
series1.add(40.0, 13984.3);
final XYSeriesCollection collection = new XYSeriesCollection();
collection.addSeries(series1);
final XYItemRenderer renderer1 = new StandardXYItemRenderer();
final NumberAxis rangeAxis1 = new NumberAxis("Range 1");
final XYPlot subplot1 = new XYPlot( collection, null, rangeAxis1, renderer1);
final CombinedDomainXYPlot plot = new CombinedDomainXYPlot(new NumberAxis("Domain"));
plot.add(subplot1, 1);
JFreeChart chart = new JFreeChart(" Demo", JFreeChart.DEFAULT_TITLE_FONT, plot, true);
final ChartPanel panel = new ChartPanel(chart, true, true, true, false, true);
panel.setPopupMenu( null );
setContentPane(panel); pack(); setVisible(true);
}
}
`

JFreeChart - How to remove gaps between XYBarRenderer in a TimeSeriesChart

I am trying to remove the gaps between the bars on a XYBarRenderer in a TimeSeriesChart. In other words, I would like to expand the bar when there is no data before and after the bar's time. Is it possible? I will really appreciate your help.
here is my code:
protected JFreeChart criarChart(XYDataset dataset){
JFreeChart chart;
chart = ChartFactory.createTimeSeriesChart(
this.getTitulo(), //titulo
this.getEixoX(), //nome do eixo-x
this.getEixoY(), //nome do eixo-y
dataset, //dados
true, //criar legenda?
true, //criar tooltips?
false); //criar URLs?
chart.setBackgroundPaint(Color.white);
XYPlot plot = (XYPlot) chart.getPlot();
plot.setOrientation(PlotOrientation.VERTICAL);
plot.setBackgroundPaint(Color.lightGray);
plot.setDomainGridlinePaint(Color.white);
plot.setRangeGridlinePaint(Color.white);
plot.setDomainCrosshairVisible(true);
plot.setRangeCrosshairVisible(true);
plot.getRenderer().setSeriesPaint(0, Color.red);
plot.setRenderer(new ClusteredXYBarRenderer() {
#Override
public Paint getItemPaint(int series, int item) {
XYDataset dataset = getPlot().getDataset();
if (dataset.getYValue(series, item) >= 0.0) {
return Color.green;
}
else {
return Color.red;
}
}
}
);
XYItemRenderer renderer = plot.getRenderer();
if(renderer instanceof XYBarRenderer){
XYBarRenderer r = (XYBarRenderer)renderer;
r.setBarPainter(new StandardXYBarPainter());
r.setMargin(-20.0);
r.setShadowVisible(false);
}
//mostra o tooltip das barras do grafico
plot.getRenderer().setBaseToolTipGenerator(new StandardXYToolTipGenerator(
StandardXYToolTipGenerator.DEFAULT_TOOL_TIP_FORMAT,
new SimpleDateFormat("HH:mm"), new DecimalFormat("#0")));
DateAxis axis = (DateAxis) plot.getDomainAxis();
axis.setDateFormatOverride(new SimpleDateFormat("HH:mm"));
return chart;
}
I saw somewhere to change the Margin, so I tried to use:
r.setMargin(-20.0);
but didn't work.
The XYBarRenderer relies on the dataset to supply values that determine the width of the bars (see the getStartXValue() and getEndXValue() methods). It also trims the width by a percentage referred to as the 'margin'. The margin is only used if it is greater than zero, and you would specify a number like 0.20 (twenty percent). The default margin is 0.0.

JFreeChart: How to plot an array of 100000 samples using JFreeChart in a dynamic fashion [duplicate]

I have an array of 100,000 samples all of double type. I want to display or plot this array so that I get a moving chart/ plot (dynamic) instead of displaying it at once. Can anyone help me out. In plot ee[] and y[] is obtained after some processing.
private byte[] FileR(String filename) {
byte[] data = null;
AudioInputStream ais;
try {
File fileIn = new File(filename);
if (fileIn.exists()) {
ais = AudioSystem.getAudioInputStream(fileIn);
data = new byte[ais.available()];
ais.read(data);
}
} catch (UnsupportedAudioFileException | IOException e) {
System.out.println(e.getMessage());
throw new RuntimeException("Could not read " + filename);
}
return data;
}
private byte[] Capture(double t) throws LineUnavailableException {
AudioFormat format = new AudioFormat(48000, 16, 2, true, false);
DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
line = (TargetDataLine) AudioSystem.getLine(info);
line.open(format);
line.open();
int size = (int) (line.getBufferSize() * t);
byte[] b = new byte[size];
line.start();
line.read(b, 0, size);
return b;
}
private void plot(double[] ee, double[] y) {
XYSeries see = new XYSeries("Filtered");
for (int i = 0; i < ee.length; i++) {
see.add(i, ee[i]);
}
XYSeriesCollection cee = new XYSeriesCollection();
cee.addSeries(see);
XYItemRenderer ree = new StandardXYItemRenderer();
NumberAxis rangeAxisee = new NumberAxis("Filtered");
XYPlot subplot1 = new XYPlot(cee, null, rangeAxisee, ree);
subplot1.setRangeAxisLocation(AxisLocation.BOTTOM_OR_LEFT);
XYSeries sy = new XYSeries("Noisy");
for (int i = 0; i < y.length; i++) {
sy.add(i, y[i]);
}
XYSeriesCollection cy = new XYSeriesCollection();
cy.addSeries(sy);
XYItemRenderer ry = new StandardXYItemRenderer();
NumberAxis rangeAxisy = new NumberAxis("Noisy");
XYPlot subplot2 = new XYPlot(cy, null, rangeAxisy, ry);
subplot2.setRangeAxisLocation(AxisLocation.BOTTOM_OR_LEFT);
CombinedDomainXYPlot plot = new CombinedDomainXYPlot(new NumberAxis("Domain"));
plot.setGap(10.0);
plot.add(subplot1);
plot.add(subplot2);
plot.setOrientation(PlotOrientation.VERTICAL);
JFreeChart chart = new JFreeChart("Adaptive Filter", JFreeChart.DEFAULT_TITLE_FONT, plot, true);
panel = new ChartPanel(chart, true, true, true, false, true);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(750, 500);
frame.add(panel, BorderLayout.CENTER);
frame.setVisible(true);
}
You need to have a thread where all this data is coming from. For example from your backend. Then, every time there is a new set of data for the chart you will need to update the chart via the Event Dispatch Thread. If your chart data is coming in regular intervals it is fairly easy (ie. pull), however if it is push (ie. the data is more random), and can get a little more tricky.
Remove all the GUI creation out of the plot method :
JFreeChart chart = new JFreeChart("Adaptive Filter", JFreeChart.DEFAULT_TITLE_FONT, plot, true);
panel = new ChartPanel(chart, true, true, true, false, true);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(750, 500);
frame.add(panel, BorderLayout.CENTER);
frame.setVisible(true);
This only needs to be called once. The plot method will be called every time new data comes.
Here is a simple approach :
public void startCharting() {
final MySoundCard card = new MySoundCard();
final MyJFreeChart chart = new MyJFreeChart();
Runnable r = new Runnable() {
#Override
public void run() {
while(true) {
int[] i = card.FileR();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
chart.plot();
}
});
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread t = new Thread(r);
t.start();
}
A thread calls your datasource every second and then updates the chart. The updates are invoked in the Event Dispatch Thread.

Resources